////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2001-2005.
// -------------------------------------------------------------------------
//  File name:   HyperGraphView.cpp
//  Version:     v1.00
//  Created:     21/3/2005 by Timur.
//  Compilers:   Visual Studio.NET 2003
//  Description: 
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include "HyperGraphView.h"
#include "HyperGraphManager.h"
#include "Controls\MemDC.h"
#include "Controls\PropertyCtrl.h"

#include "HyperGraphDialog.h"
#include "clipboard.h"

#include "FlowGraphNode.h"
#include "CommentNode.h"
#include "CommentBoxNode.h"
#include "BlackBoxNode.h"
#include "TrackEventNode.h"
#include "QuickSearchNode.h"

#include "FlowGraphManager.h"
#include "FlowGraph.h"
#include "FlowGraphVariables.h"
#include "GameEngine.h"
#include "SegmentedWorld/SegmentedWorldManager.h"

#include <Gdiplus.h>
#include "Objects/EntityObject.h"
#include "HyperGraph/FlowGraphDebuggerEditor.h"

#include "Viewport.h"

enum{
	ID_GRAPHVIEW_RENAME=1,
	ID_GRAPHVIEW_ADD_SELECTED_ENTITY,
	ID_GRAPHVIEW_ADD_DEFAULT_ENTITY,
	ID_GRAPHVIEW_TARGET_SELECTED_ENTITY,
	ID_GRAPHVIEW_TARGET_GRAPH_ENTITY,
	ID_GRAPHVIEW_TARGET_GRAPH_ENTITY2,
	//ID_GRAPHVIEW_BLACKBOX_SELECTED_ENTITY,
	ID_GRAPHVIEW_ADD_COMMENT,
	ID_GRAPHVIEW_ADD_COMMENTBOX,
	ID_GRAPHVIEW_FIT_TOVIEW,
	ID_GRAPHVIEW_SELECT_ENTITY,
	ID_GRAPHVIEW_SELECT_AND_GOTO_ENTITY,
	ID_GRAPHVIEW_SPLINES,
	ID_GRAPHVIEW_ADD_BLACK_BOX,
	ID_GRAPHVIEW_ADD_TRACKEVENT,
	ID_GRAPHVIEW_ADD_STARTNODE,
	ID_GRAPHVIEW_ADD_BREAKPOINT,
	ID_GRAPHVIEW_REMOVE_BREAKPOINT,
	ID_GRAPHVIEW_REMOVE_BREAKPOINTS_FOR_NODE,
	ID_GRAPHVIEW_REMOVE_ALL_BREAKPOINTS,
	ID_BREAKPOINT_ENABLE,
	ID_TRACEPOINT_ENABLE,
	ID_INPUTS_SHOW_ALL,
	ID_INPUTS_HIDE_ALL,
	ID_OUTPUTS_SHOW_ALL,
	ID_OUTPUTS_HIDE_ALL,
	ID_INPUTS_DISABLE_LINKS,
	ID_INPUTS_ENABLE_LINKS,
	ID_OUTPUTS_DISABLE_LINKS,
	ID_OUTPUTS_ENABLE_LINKS,
	ID_INPUTS_DELETE_LINKS,
	ID_OUTPUTS_DELETE_LINKS,
	ID_INPUTS_CUT_LINKS,
	ID_INPUTS_COPY_LINKS,
	ID_INPUTS_PASTE_LINKS,
	ID_OUTPUTS_CUT_LINKS,
	ID_OUTPUTS_COPY_LINKS,
	ID_OUTPUTS_PASTE_LINKS,
};



#define BASE_NEW_NODE_CMD 10000
#define BASE_INPUTS_CMD 20000
#define BASE_OUTPUTS_CMD 21000

#define GET_GDI_COLOR(x) (Gdiplus::Color(GetRValue(x),GetGValue(x),GetBValue(x)))

//////////////////////////////////////////////////////////////////////////
#define BACKGROUND_COLOR (gSettings.hyperGraphColors.colorBackground)
#define GRID_COLOR			 (gSettings.hyperGraphColors.colorGrid)
#define GRID_SIZE 10
#define ARROW_COLOR GET_GDI_COLOR(gSettings.hyperGraphColors.colorArrow)
#define ARROW_SEL_COLOR_IN GET_GDI_COLOR(gSettings.hyperGraphColors.colorInArrowHighlighted)
#define ARROW_SEL_COLOR_OUT GET_GDI_COLOR(gSettings.hyperGraphColors.colorOutArrowHighlighted)
#define PORT_EDGE_HIGHLIGHT GET_GDI_COLOR(gSettings.hyperGraphColors.colorPortEdgeHighlighted)
#define ARROW_DIS_COLOR GET_GDI_COLOR(gSettings.hyperGraphColors.colorArrowDisabled)
#define ARROW_WIDTH 14

#define MIN_ZOOM 0.01f
#define MAX_ZOOM 100.0f
#define MIN_ZOOM_CAN_EDIT 0.8f
#define MIN_ZOOM_CHANGE_HEIGHT 1.2f

#define DEFAULT_EDIT_HEIGHT 14

#define SMALLNODE_SPLITTER 40
#define SMALLNODE_WIDTH 160

#define PORT_HEIGHT 16

//////////////////////////////////////////////////////////////////////////
// CHyperGraphViewQuickSearchEdit implementation.
//////////////////////////////////////////////////////////////////////////
BEGIN_MESSAGE_MAP (CHyperGraphViewQuickSearchEdit,CEdit)
	ON_WM_GETDLGCODE()
	ON_WM_KILLFOCUS()
	ON_CONTROL_REFLECT(EN_KILLFOCUS, OnSearchKillFocus)
END_MESSAGE_MAP ()

//////////////////////////////////////////////////////////////////////////
BOOL CHyperGraphViewQuickSearchEdit::PreTranslateMessage(MSG* msg)
{
	if(!m_pView)
		return TRUE;

	if (msg->message == WM_LBUTTONDOWN || msg->message == WM_RBUTTONDOWN)
	{
		CPoint pnt;
		GetCursorPos(&pnt);
		CRect rc;
		GetWindowRect(rc);
		if (!rc.PtInRect(pnt))
		{
			// Accept edit.
			m_pView->OnAcceptSearch();
			return TRUE;
		}
	}
	else if (msg->message == WM_KEYDOWN)
	{
		switch (msg->wParam)
		{
		case VK_UP:
			return TRUE;
			break;
		}
	}
	else if (msg->message == WM_KEYUP)
	{
		switch (msg->wParam)
		{
		case VK_RETURN:
			{
				GetAsyncKeyState(VK_CONTROL);
				if (!GetAsyncKeyState(VK_CONTROL))
				{
					// Send accept message.
					m_pView->OnAcceptSearch();
					return TRUE;
				}
			}
			break;

		case VK_ESCAPE:
			// Cancel edit.
			m_pView->OnCancelSearch();
			return TRUE;
			break;
		case VK_UP:
			m_pView->OnSelectNextSearchResult(true);
			return TRUE;
			break;
		case VK_DOWN:
		case VK_TAB:
			m_pView->OnSelectNextSearchResult(false);
			return TRUE;
			break;
		default:
			if((msg->wParam >= 'A' && msg->wParam <= 'Z') || msg->wParam == VK_BACK)
				m_pView->OnSearchNodes();
			break;
		}	
	}
	return CEdit::PreTranslateMessage(msg);
}

void CHyperGraphViewQuickSearchEdit::OnKillFocus(CWnd *pWnd)
{
	// Send accept
	if(m_pView)
		m_pView->OnCancelSearch();

	CEdit::OnKillFocus(pWnd);
}

void CHyperGraphViewQuickSearchEdit::OnSearchKillFocus()
{
	// Send accept
	if(m_pView)
		m_pView->OnAcceptSearch();
}

//////////////////////////////////////////////////////////////////////////
// CHyperGraphViewRenameEdit implementation.
//////////////////////////////////////////////////////////////////////////
BEGIN_MESSAGE_MAP (CHyperGraphViewRenameEdit,CEdit)
	ON_WM_GETDLGCODE()
	ON_WM_KILLFOCUS()
	ON_CONTROL_REFLECT(EN_KILLFOCUS, OnEditKillFocus)
END_MESSAGE_MAP ()

//////////////////////////////////////////////////////////////////////////
BOOL CHyperGraphViewRenameEdit::PreTranslateMessage(MSG* msg)
{
	if (msg->message == WM_LBUTTONDOWN || msg->message == WM_RBUTTONDOWN)
	{
		CPoint pnt;
		GetCursorPos(&pnt);
		CRect rc;
		GetWindowRect(rc);
		if (!rc.PtInRect(pnt))
		{
			// Accept edit.
			m_pView->OnAcceptRename();
			return TRUE;
		}
	}
	else if (msg->message == WM_KEYDOWN)
	{
		switch (msg->wParam)
		{
		case VK_RETURN:
			{
				GetAsyncKeyState(VK_CONTROL);
				if (!GetAsyncKeyState(VK_CONTROL))
				{
					// Send accept message.
					m_pView->OnAcceptRename();
					return TRUE;
				}

				/*
				// Add carriege return into the end of string.
				const char *szStr = "\n";
				int len = GetWindowTextLength();
				SendMessage( EM_SETSEL,len,-1 );
				SendMessage( EM_SCROLLCARET, 0,0 );
				SendMessage( EM_REPLACESEL, FALSE, (LPARAM)szStr );
				*/
			}
			break;

		case VK_ESCAPE:
			// Cancel edit.
			m_pView->OnCancelRename();
			return TRUE;
			break;
		}
	}
	return CEdit::PreTranslateMessage(msg);
}

void CHyperGraphViewRenameEdit::OnKillFocus(CWnd *pWnd)
{
	// Send accept
	m_pView->OnAcceptRename();
	CEdit::OnKillFocus(pWnd);
}

void CHyperGraphViewRenameEdit::OnEditKillFocus()
{
	// Send accept
	m_pView->OnAcceptRename();
}



//////////////////////////////////////////////////////////////////////////
// CEditPropertyCtrl implementation.
//////////////////////////////////////////////////////////////////////////
BEGIN_MESSAGE_MAP (CEditPropertyCtrl,CPropertyCtrl)
	ON_WM_KEYDOWN()
END_MESSAGE_MAP ()


void CEditPropertyCtrl::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	CPropertyCtrl::OnKeyDown(nChar, nRepCnt, nFlags);

	switch (nChar)
	{
	case VK_RETURN:
		m_pView->HideEditPort();
		break;
	}
}

void CEditPropertyCtrl::SelectItem( CPropertyItem* item )
{
	CPropertyCtrl::SelectItem( item );
	if(!item)
		m_pView->HideEditPort();
}

//////////////////////////////////////////////////////////////////////////
void CEditPropertyCtrl::Expand( CPropertyItem *item,bool bExpand,bool bRedraw )
{
	// Avoid parent method calling
}


//////////////////////////////////////////////////////////////////////////
IMPLEMENT_DYNCREATE(CHyperGraphView,CView)

BEGIN_MESSAGE_MAP (CHyperGraphView, CView)
	ON_WM_MOUSEMOVE()
	ON_WM_DESTROY()
	ON_WM_ERASEBKGND()
	ON_WM_PAINT()
	ON_WM_SIZE()
	ON_WM_MOUSEWHEEL()
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_WM_RBUTTONDOWN()
	ON_WM_RBUTTONUP()
	ON_WM_MBUTTONDOWN()
	ON_WM_MBUTTONUP()
	ON_WM_LBUTTONDBLCLK()
	ON_WM_KEYDOWN()
	ON_WM_KEYUP()
	ON_WM_SETCURSOR()
	ON_WM_HSCROLL()
	ON_WM_VSCROLL()

	ON_COMMAND(ID_EDIT_DELETE,OnCommandDelete)
	ON_COMMAND(ID_EDIT_COPY,OnCommandCopy)
	ON_COMMAND(ID_EDIT_PASTE,OnCommandPaste)
	ON_COMMAND(ID_EDIT_PASTE_WITH_LINKS,OnCommandPasteWithLinks)
	ON_COMMAND(ID_EDIT_CUT,OnCommandCut)
	//ON_COMMAND(ID_MINIMIZE,OnToggleMinimize)
	ON_COMMAND(ID_EDIT_DELETE_KEEP_LINKS,OnCommandDeleteKeepLinks)
END_MESSAGE_MAP ()

struct GraphicsHelper
{
	GraphicsHelper( CWnd * pWnd ) : m_pWnd(pWnd), m_pDC(pWnd->GetDC()), m_pGr(new Gdiplus::Graphics(m_pDC->GetSafeHdc()))
	{
	}
	~GraphicsHelper()
	{
		delete m_pGr;
		m_pWnd->ReleaseDC(m_pDC);
	}

	operator Gdiplus::Graphics *()
	{
		return m_pGr;
	}

	CWnd * m_pWnd;
	CDC * m_pDC;
	Gdiplus::Graphics * m_pGr;
};

//////////////////////////////////////////////////////////////////////////
CHyperGraphView::CHyperGraphView()
{
	m_pGraph = 0;
	m_mouseDownPos = CPoint(0,0);
	m_scrollOffset = CPoint(0,0);
	m_rcSelect.SetRect(0,0,0,0);
	m_mode = NothingMode;

	m_pPropertiesCtrl = 0;
	m_isEditPort = false;
	m_zoom = 1.0f;
	m_bSplineArrows = true;
	m_bHighlightIncomingEdges = false;
	m_bHighlightOutgoingEdges = false;
	m_bCopiedBlackBox = false;
	m_componentViewMask = 0; // will be set by dialog
	m_bRefitGraphInOnSize = false;
	m_pEditPort = 0;
	m_bIsFrozen = false;
	m_pQuickSearchNode = NULL;
}

//////////////////////////////////////////////////////////////////////////
CHyperGraphView::~CHyperGraphView()
{
	SetGraph( NULL );
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::SetPropertyCtrl( CPropertyCtrl *propsCtrl )
{
	m_pPropertiesCtrl = propsCtrl;

	if (m_pPropertiesCtrl)
	{
		m_pPropertiesCtrl->SetUpdateCallback( functor(*this,&CHyperGraphView::OnUpdateProperties) );
		m_pPropertiesCtrl->EnableUpdateCallback(true);
	}
}

//////////////////////////////////////////////////////////////////////////
BOOL CHyperGraphView::Create( DWORD dwStyle,const RECT &rect,CWnd *pParentWnd,UINT nID )
{
	if (!m_hWnd)
	{
		//////////////////////////////////////////////////////////////////////////
		// Create window.
		//////////////////////////////////////////////////////////////////////////
		CRect rcDefault(0,0,400,400);
		LPCTSTR lpClassName = AfxRegisterWndClass(CS_DBLCLKS|CS_HREDRAW|CS_VREDRAW,	AfxGetApp()->LoadStandardCursor(IDC_ARROW), NULL, NULL);
		VERIFY( CreateEx( NULL,lpClassName,"HyperGraphView",dwStyle,rcDefault, pParentWnd, nID));

		if (!m_hWnd)
			return FALSE;

		CRect rc(0,0,220,18);
		m_editParamCtrl.Create( WS_CHILD, rc, this );
		m_editParamCtrl.SetView(this);
		m_editParamCtrl.SetFlags(CPropertyCtrl::F_NOBITMAPS);
		m_editParamCtrl.ExpandAll();
	}
	return TRUE;
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::PostNcDestroy()
{
}

void CHyperGraphView::OnDestroy() 
{
	if (m_pGraph)
		m_pGraph->RemoveListener(this);

	__super::OnDestroy();
}

//////////////////////////////////////////////////////////////////////////
BOOL CHyperGraphView::PreTranslateMessage(MSG* pMsg)
{
	if (!m_tooltip.m_hWnd)
	{
		CRect rc;
		GetClientRect(rc);
		m_tooltip.Create( this );
		m_tooltip.SetDelayTime( 1000 );
		m_tooltip.SetMaxTipWidth(600);
		m_tooltip.AddTool( this,"",rc,1 );
		m_tooltip.Activate(FALSE);
	}
	m_tooltip.RelayEvent(pMsg);

	BOOL bResult = __super::PreTranslateMessage( pMsg );
	return bResult;
}

//////////////////////////////////////////////////////////////////////////
BOOL CHyperGraphView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt) 
{
	HideEditPort();
	if(m_pQuickSearchNode)
		OnCancelSearch();

	float multiplier = 0.1f * 1.0f / 120.0f;
	BOOL bShift = (GetKeyState(VK_SHIFT) & 0x8000);
	if (bShift)
		multiplier *= 2.0f;

	// zoom around the mouse cursor position, first get old mouse pos
	GetCursorPos(&pt);
	ScreenToClient(&pt);
	Gdiplus::PointF oldZoomFocus = ViewToLocal(pt);

	// zoom
	SetZoom( m_zoom + m_zoom * multiplier * zDelta );

	// determine where the focus has moved as part of the zoom
	CPoint newZoomFocus = LocalToView(oldZoomFocus);

	// adjust offset based on displacement and update windows stuff
	m_scrollOffset-= (newZoomFocus - pt);
	SetScrollExtents(false);
	Invalidate();

	return TRUE;
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::OnSize(UINT nType, int cx, int cy)
{
	__super::OnSize( nType,cx,cy );

	//marcok: the following code handles the case when we've tried to fit the graph to view whilst not knowing the correct size of the client rect
	if (m_bRefitGraphInOnSize)
	{
		m_bRefitGraphInOnSize = false;
		OnCommandFitAll();
	}

	SetScrollExtents(false);
}

//////////////////////////////////////////////////////////////////////////
BOOL CHyperGraphView::OnEraseBkgnd(CDC* pDC) 
{
	/*
	RECT rect;
	CBrush cFillBrush;

	// Get the rect of the client window
	GetClientRect(&rect);

	// Create the brush
	cFillBrush.CreateSolidBrush(BACKGROUND_COLOR);

	// Fill the entire client area
	pDC->FillRect(&rect, &cFillBrush);
*/
	return TRUE;
}

static bool LinesegRectSingle( Gdiplus::PointF * pWhere, const Lineseg& line1, float x1, float y1, float x2, float y2 )
{
	Vec3 a(x1,y1,0);
	Vec3 b(x2,y2,0);
	Lineseg line2(a,b);
	float t1, t2;
	if (Intersect::Lineseg_Lineseg2D(line1, line2, t1, t2))
	{
		Vec3 pos = line1.start + t1 * (line1.end - line1.start);
		pWhere->X = pos.x;
		pWhere->Y = pos.y;
		return true;
	}
	return false;
}

static void LinesegRectIntersection( Gdiplus::PointF * pWhere, EHyperEdgeDirection * pDir, const Lineseg& line, const Gdiplus::RectF& rect )
{
	if (LinesegRectSingle(pWhere, line, rect.X, rect.Y, rect.X + rect.Width, rect.Y))
		*pDir = eHED_Up;
	else if (LinesegRectSingle(pWhere, line, rect.X, rect.Y, rect.X, rect.Y + rect.Height))
		*pDir = eHED_Left;
	else if (LinesegRectSingle(pWhere, line, rect.X + rect.Width, rect.Y, rect.X + rect.Width, rect.Y + rect.Height))
		*pDir = eHED_Right;
	else if (LinesegRectSingle(pWhere, line, rect.X, rect.Y + rect.Height, rect.X + rect.Width, rect.Y + rect.Height))
		*pDir = eHED_Down;
	else
	{
		*pDir = eHED_Down;
		pWhere->X = rect.X + rect.Width * 0.5f;
		pWhere->Y = rect.Y + rect.Height * 0.5f;
	}
}

void CHyperGraphView::ReroutePendingEdges()
{
  FUNCTION_PROFILER(gEnv->pSystem, PROFILE_EDITOR);

	std::vector<PendingEdges::iterator> eraseIters;

	for (PendingEdges::iterator iter = m_edgesToReroute.begin(); iter != m_edgesToReroute.end(); ++iter)
	{
		CHyperEdge * pEdge = *iter;
//		if (pEdge->nodeOut == -1)
//			DEBUG_BREAK;
		CHyperNode * pNodeOut = (CHyperNode*) m_pGraph->FindNode( pEdge->nodeOut );
		CHyperNode * pNodeIn = (CHyperNode*) m_pGraph->FindNode( pEdge->nodeIn );
		if ((!pNodeIn && m_bDraggingFixedOutput && pEdge != m_pDraggingEdge) || (!pNodeOut && !m_bDraggingFixedOutput && pEdge != m_pDraggingEdge))
			continue;
		
		Gdiplus::RectF rectOut, rectIn;
		if (pNodeIn)
		{
			if (!pNodeIn->GetAttachRect(pEdge->nPortIn, &rectIn) && !pNodeIn->GetBlackBox())
				continue;
			rectIn.Offset( pNodeIn->GetPos() );
		}
		else if (m_bDraggingFixedOutput && pEdge == m_pDraggingEdge)
		{
			rectIn = Gdiplus::RectF( m_pDraggingEdge->pointIn.X, m_pDraggingEdge->pointIn.Y, 0, 0 );
		}
		else
		{
			continue;
		}

		if (pNodeOut)
		{
			if (!pNodeOut->GetAttachRect(pEdge->nPortOut + 1000, &rectOut) && !pNodeOut->GetBlackBox())
				continue;
			rectOut.Offset( pNodeOut->GetPos() );
		}
		else if (!m_bDraggingFixedOutput && pEdge == m_pDraggingEdge)
		{
			rectOut = Gdiplus::RectF( m_pDraggingEdge->pointOut.X, m_pDraggingEdge->pointOut.Y, 0, 0 );
		}
		else
		{
			continue;
		}

		if (rectOut.IsEmptyArea() && rectIn.IsEmptyArea())
		{
			rectOut.GetLocation( &pEdge->pointOut );
			rectIn.GetLocation( &pEdge->pointIn );
			// hack... but it works
			pEdge->dirOut = eHED_Right;
			pEdge->dirIn = eHED_Left;
		}
		else if (!rectOut.IsEmptyArea() && !rectIn.IsEmptyArea())
		{
			Vec3 centerOut( rectOut.X + rectOut.Width * 0.5f, rectOut.Y + rectOut.Height * 0.5f, 0.0f );
			Vec3 centerIn( rectIn.X + rectIn.Width * 0.5f, rectIn.Y + rectIn.Height * 0.5f, 0.0f );
			Vec3 dir = centerOut - centerIn;
			dir.Normalize();
			dir *= 10.0f;
			std::swap(dir.x, dir.y);
			dir.x = -dir.x;
			centerIn += dir;
			centerOut += dir;
			Lineseg centerLine( centerOut, centerIn );
			LinesegRectIntersection( &pEdge->pointOut, &pEdge->dirOut, centerLine, rectOut );
			LinesegRectIntersection( &pEdge->pointIn, &pEdge->dirIn, centerLine, rectIn );
		}
		else if (!rectOut.IsEmptyArea() && rectIn.IsEmptyArea())
		{
			Vec3 centerOut( rectOut.X + rectOut.Width * 0.5f, rectOut.Y + rectOut.Height * 0.5f, 0.0f );
			Vec3 centerIn( rectIn.X + rectIn.Width * 0.5f, rectIn.Y + rectIn.Height * 0.5f, 0.0f );
			Lineseg centerLine( centerOut, centerIn );
			LinesegRectIntersection( &pEdge->pointOut, &pEdge->dirOut, centerLine, rectOut );
			rectIn.GetLocation( &pEdge->pointIn );
			pEdge->dirIn = eHED_Left;
		}
		else if (rectOut.IsEmptyArea() && !rectIn.IsEmptyArea())
		{
			Vec3 centerOut( rectOut.X + rectOut.Width * 0.5f, rectOut.Y + rectOut.Height * 0.5f, 0.0f );
			Vec3 centerIn( rectIn.X + rectIn.Width * 0.5f, rectIn.Y + rectIn.Height * 0.5f, 0.0f );
			Lineseg centerLine( centerOut, centerIn );
			LinesegRectIntersection( &pEdge->pointIn, &pEdge->dirIn, centerLine, rectIn );
			rectOut.GetLocation( &pEdge->pointOut );
			pEdge->dirOut = eHED_Right;
		}
		else
		{
			assert(false);
		}

		eraseIters.push_back( iter );
	}
	while (!eraseIters.empty())
	{
		m_edgesToReroute.erase(eraseIters.back());
		eraseIters.pop_back();
	}
}

void CHyperGraphView::RerouteAllEdges()
{
	std::vector<CHyperEdge*> edges;
	m_pGraph->GetAllEdges(edges);
	std::copy( edges.begin(), edges.end(), std::inserter(m_edgesToReroute, m_edgesToReroute.end()) );

	std::set<CHyperNode*> nodes;
	GraphicsHelper g(this);
	for (std::vector<CHyperEdge*>::const_iterator iter = edges.begin(); iter != edges.end(); ++iter)
	{
		CHyperNode * pNodeIn = (CHyperNode*) m_pGraph->FindNode( (*iter)->nodeIn );
		CHyperNode * pNodeOut = (CHyperNode*) m_pGraph->FindNode( (*iter)->nodeOut );
		if (pNodeIn && pNodeOut)
		{
			nodes.insert( pNodeIn );
			nodes.insert( pNodeOut );
		}
	}
	for (std::set<CHyperNode*>::const_iterator iter = nodes.begin(); iter != nodes.end(); ++iter)
	{
		(*iter)->Draw( this, *(Gdiplus::Graphics*)g, false );
	}
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::OnPaint()
{
  FUNCTION_PROFILER(gEnv->pSystem, PROFILE_EDITOR);

	CPaintDC PaintDC(this); // device context for painting

	CRect rc = PaintDC.m_ps.rcPaint;
	Gdiplus::Bitmap backBufferBmp(rc.Width(),rc.Height());

	Gdiplus::Graphics mainGr( PaintDC.GetSafeHdc() );

	Gdiplus::Graphics* pBackBufferGraphics = Gdiplus::Graphics::FromImage(&backBufferBmp);
	Gdiplus::Graphics &gr = *pBackBufferGraphics;

	gr.TranslateTransform( m_scrollOffset.x - rc.left, m_scrollOffset.y - rc.top );
	gr.ScaleTransform( m_zoom, m_zoom );
	gr.SetSmoothingMode(Gdiplus::SmoothingModeNone);
	gr.SetTextRenderingHint(Gdiplus::TextRenderingHintAntiAliasGridFit );
	//gr.SetTextRenderingHint(Gdiplus::TextRenderingHintSystemDefault);

	CRect rect = PaintDC.m_ps.rcPaint;
	Gdiplus::RectF clip = ViewToLocalRect(rect);

	// Create the brush
//	Gdiplus::SolidBrush fillBrush(BACKGROUND_COLOR);
	gr.Clear( BACKGROUND_COLOR );

//	Gdiplus::Pen border( Gdiplus::Color(0,255,0), 5.0f );
//	gr.DrawRectangle( &border, clip );

//	gr.FillRectangle( &fillBrush, clip );

	gr.SetSmoothingMode(Gdiplus::SmoothingModeNone);
	if (m_pGraph)
	{
		DrawGrid( gr,rect );
		gr.SetSmoothingMode(Gdiplus::SmoothingModeHighQuality);
		ReroutePendingEdges();
		DrawEdges( gr,rect );
		gr.SetSmoothingMode(Gdiplus::SmoothingModeNone);
		DrawNodes( gr,rect );
		
		if (m_pDraggingEdge)
		{
			DrawArrow( gr, m_pDraggingEdge, 0 );
		}
	}

	if(m_bIsFrozen)
	{
		Gdiplus::SolidBrush fillBrush( (BACKGROUND_COLOR & 0x00FFFFFF) | 0x80000000);
		gr.FillRectangle( &fillBrush, clip );
	}

	mainGr.DrawImage(&backBufferBmp,rc.left,rc.top,rc.Width(),rc.Height());
	delete pBackBufferGraphics;

//	if (!m_edgesToReroute.empty())
//		Invalidate();
}

//////////////////////////////////////////////////////////////////////////
bool CHyperGraphView::HitTestEdge( CPoint pnt,CHyperEdge* &pOutEdge,int &nIndex )
{
	if (!m_pGraph)
		return false;

	const float edgeSelectionThreshold = gSettings.fHyperGraphEdgeSelectionThreshold;

	Gdiplus::PointF mousePt = ViewToLocal(pnt);

	std::vector<CHyperEdge*> edges;
	m_pGraph->GetAllEdges( edges );
	{
		for (int i = 0; i < edges.size(); i++)
		{
			CHyperEdge *pEdge = edges[i];
			for (int j = 0; j < 4; j++)
			{
				float dx = pEdge->cornerPoints[j].X - mousePt.X;
				float dy = pEdge->cornerPoints[j].Y - mousePt.Y;

				if ((dx*dx + dy*dy) < edgeSelectionThreshold)
				{
					pOutEdge = pEdge;
					nIndex = j;
					return true;
				}
			}
		}
	}
	return false;
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::OnLButtonDown(UINT nFlags, CPoint point) 
{
	if (!m_pGraph)
		return;

	HideEditPort();

	m_pHitEdge = 0;

	SetFocus();
	// Save the mouse down position
	m_mouseDownPos = point;


	if(m_bIsFrozen)
		return;

	SW_TEST_GRAPH_MOD(m_pGraph, true)

	bool bAltClick = CheckVirtualKey(VK_MENU);
	bool bCtrlClick = (nFlags & MK_CONTROL);
	bool bShiftClick = (nFlags & MK_SHIFT);

	Gdiplus::PointF mousePt = ViewToLocal(point);

	CHyperEdge *pHitEdge = 0;
	int nHitEdgePoint = 0;
	if (HitTestEdge( point,pHitEdge,nHitEdgePoint ))
	{
		m_pHitEdge = pHitEdge;
		m_nHitEdgePoint = nHitEdgePoint;
		m_prevCornerW = m_pHitEdge->cornerW;
		m_prevCornerH = m_pHitEdge->cornerH;
		m_mode = EdgePointDragMode;
		CaptureMouse();
		return;
	}
	
	CHyperNode *pNode = GetMouseSensibleNodeAtPoint( point );
	if (pNode)
	{
		int objectClicked = pNode->GetObjectAt( GraphicsHelper(this), ViewToLocal(point) );

		CHyperNodePort * pPort = NULL;
		static const int WAS_A_PORT = -10000;
		static const int WAS_A_SELECT = -10001;

		if (objectClicked >= eSOID_ShiftFirstOutputPort && objectClicked <= eSOID_ShiftLastOutputPort)
		{
			if (bShiftClick)
				objectClicked = eSOID_FirstOutputPort + (objectClicked - eSOID_ShiftFirstOutputPort);
			else if (bAltClick)
				objectClicked = eSOID_FirstOutputPort + (objectClicked - eSOID_ShiftFirstOutputPort);
			else
				objectClicked = eSOID_Background;
		}

		if ((bAltClick && (objectClicked < eSOID_FirstOutputPort && objectClicked > eSOID_LastOutputPort)) || bCtrlClick)
		{
			objectClicked = WAS_A_SELECT;
		}
		else if (objectClicked >= eSOID_FirstInputPort && objectClicked <= eSOID_LastInputPort && !pNode->GetInputs()->empty())
		{
			pPort = &pNode->GetInputs()->at(objectClicked-eSOID_FirstInputPort);
			objectClicked = WAS_A_PORT;
		}
		else if (objectClicked >= eSOID_FirstOutputPort && objectClicked <= eSOID_LastOutputPort && !pNode->GetOutputs()->empty())
		{
			pPort = &pNode->GetOutputs()->at(objectClicked-eSOID_FirstOutputPort);
			objectClicked = WAS_A_PORT;
		}
		else if (strcmp(pNode->GetClassName(), CBlackBoxNode::GetClassType()) == 0)
		{
			pPort = ((CBlackBoxNode*)(pNode))->GetPortAtPoint(ViewToLocal(point));
			if(pPort)
			{
				objectClicked = WAS_A_PORT;
				pNode = ((CBlackBoxNode*)(pNode))->GetNode(pPort);
			}
			else if(objectClicked == eSOID_Minimize)
			{
				((CBlackBoxNode*)(pNode))->Minimize();
				objectClicked = eSOID_Background;
			}
			else if(objectClicked == -1)
			{
				CBlackBoxNode *pBB = (CBlackBoxNode*)pNode;
				if(pBB->IsMinimized() == false)
				{
					std::vector<CHyperNode*>* nodes = pBB->GetNodes();
					for(int i = 0; i < nodes->size(); ++i)
					{
						int clicked = ((*nodes)[i])->GetObjectAt( GraphicsHelper(this), ViewToLocal(point) );
						if(clicked > 0)
						{
							pNode = (*nodes)[i];
							objectClicked = clicked;
							break;
						}
					}
				}
			}
		}
		switch (objectClicked)
		{
		case eSOID_InputTransparent:
			break;
		case WAS_A_SELECT:
		case eSOID_AttachedEntity:
		case eSOID_Background:
		case eSOID_Title:
			{
				bool bUnselect = bCtrlClick && pNode->IsSelected();
				if (!pNode->IsSelected() || bUnselect)
				{
					CUndo undo( "Select Graph Node(s)" );
					m_pGraph->RecordUndo();
					// Select this node if not selected yet.
					if (!bCtrlClick && !bUnselect)
						m_pGraph->UnselectAll();
					//pNode->SetSelected( !bUnselect );
					IHyperGraphEnumerator* pEnum = pNode->GetRelatedNodesEnumerator();
					for (IHyperNode* pRelative = pEnum->GetFirst(); pRelative; pRelative = pEnum->GetNext())
						static_cast<CHyperNode*>(pRelative)->SetSelected(!bUnselect);
					pEnum->Release();
					OnSelectionChange();
				}
				m_mode = MoveMode;
				m_moveHelper.clear();
				GetIEditor()->BeginUndo();
			}
			break;
		case eSOID_InputGripper:
			ShowPortsConfigMenu( point,true,pNode );
			break;
		case eSOID_OutputGripper:
			ShowPortsConfigMenu( point,false,pNode );
			break;
		case eSOID_Minimize:
			OnToggleMinimizeNode(pNode);
			break;
		case eSOID_Border_UpRight:
		case eSOID_Border_Right:
		case eSOID_Border_DownRight:
		case eSOID_Border_Down:
		case eSOID_Border_DownLeft:
		case eSOID_Border_Left:
		case eSOID_Border_UpLeft:
		case eSOID_Border_Up:
		{
			if (pNode->GetResizeBorderRect())
			{
				m_mode = NodeBorderDragMode;
				m_DraggingBorderNodeInfo.m_pNode = pNode;
				m_DraggingBorderNodeInfo.m_border = objectClicked;
				m_DraggingBorderNodeInfo.m_origNodePos = pNode->GetPos();
				m_DraggingBorderNodeInfo.m_origBorderRect = *pNode->GetResizeBorderRect();
				m_DraggingBorderNodeInfo.m_clickPoint = mousePt;
			}
			break;
		}

		default:
			//assert(false);
			break;

		case WAS_A_PORT:

			if (!bAltClick && (!pPort->bInput || pPort->nConnected <= 1))
			{				
				if (!pNode->IsSelected())
				{
					CUndo undo( "Select Graph Node(s)" );
					m_pGraph->RecordUndo();
					// Select this node if not selected yet.
					m_pGraph->UnselectAll();
					pNode->SetSelected( true );
					OnSelectionChange();
				}

				if (pPort->bInput && !pPort->nConnected && pPort->pVar->GetType()!=IVariable::UNKNOWN && m_zoom > MIN_ZOOM_CAN_EDIT)
				{
					Gdiplus::RectF rect = pNode->GetRect();
					if(mousePt.X - rect.X > ARROW_WIDTH)
						m_pEditPort = pPort;
				}

				// Undo must start before edge delete.
				GetIEditor()->BeginUndo();
				m_pGraph->RecordUndo();
				pPort->bSelected = true;

				// If trying to drag output port, disconnect input parameter and drag it.
				if (pPort->bInput)
				{
					CHyperEdge *pEdge =	m_pGraph->FindEdge( pNode,pPort );
					if (pEdge)
					{
						// Disconnect this edge.
						pNode = (CHyperNode*)m_pGraph->FindNode(pEdge->nodeOut);
						if (pNode)
						{
							pPort = &pNode->GetOutputs()->at(pEdge->nPortOut);
							InvalidateEdge(pEdge);
							m_pGraph->RemoveEdge( pEdge );
						}
						else
							pPort = 0;
						m_bDraggingFixedOutput = true;
					}
					else
					{
						m_bDraggingFixedOutput = false;
					}
				}
				else
				{
					m_bDraggingFixedOutput = true;
				}
				if (!pPort || !pNode)
				{
					GetIEditor()->CancelUndo();
					return;
				}

				m_mode = PortDragMode;

				m_pDraggingEdge = m_pGraph->CreateEdge();
				m_edgesToReroute.insert(m_pDraggingEdge);

				m_pDraggingEdge->nodeIn = -1;
				m_pDraggingEdge->nPortIn = -1;
				m_pDraggingEdge->nodeOut = -1;
				m_pDraggingEdge->nPortOut = -1;

				if (m_bDraggingFixedOutput)
				{
					m_pDraggingEdge->nodeOut = pNode->GetId();
					m_pDraggingEdge->nPortOut = pPort->nPortIndex;
					m_pDraggingEdge->pointIn = ViewToLocal(point);
				}
				else
				{
					m_pDraggingEdge->nodeIn = pNode->GetId();
					m_pDraggingEdge->nPortIn = pPort->nPortIndex;
					m_pDraggingEdge->pointOut = ViewToLocal(point);
				}
				pPort->bSelected = true;

				InvalidateNode(pNode,true);
			}
			else
			{
				CMenu menu;
				menu.CreatePopupMenu();
				std::vector<CHyperEdge*> edges;
				m_pGraph->GetAllEdges(edges);
				for (size_t i=0; i<edges.size(); ++i)
				{
					if (edges[i]->nodeIn == pNode->GetId() && 0 == stricmp(edges[i]->portIn, pPort->GetName()))
					{
						CString title;
						CHyperNode * pToNode = (CHyperNode*) m_pGraph->FindNode( edges[i]->nodeOut );
						title.Format("Remove %s:%s", pToNode->GetClassName(), edges[i]->portOut);
						menu.AppendMenu(MF_STRING, i+1, title);
					}
				}
				CPoint pt;
				GetCursorPos(&pt);
				int nEdge = menu.TrackPopupMenuEx(TPM_RETURNCMD, pt.x, pt.y, this, NULL);
				if (nEdge)
				{
					nEdge--;
					m_pGraph->RemoveEdge( edges[nEdge] );
				}
			}
			break;
		}
	}
	else
	{
		if (!bAltClick && !bCtrlClick && m_pGraph->IsSelectedAny())
		{
			CUndo undo( "Unselect Graph Node(s)" );
			m_pGraph->RecordUndo();
			m_pGraph->UnselectAll();
			OnSelectionChange();
		}
		m_mode = SelectMode;
	}

	CaptureMouse();

	//CWnd::OnLButtonDown(nFlags, point);
}

//////////////////////////////////////////////////////////////////////////
bool CHyperGraphView::CheckVirtualKey( int virtualKey )
{
	GetAsyncKeyState(virtualKey);
	if (GetAsyncKeyState(virtualKey))
		return true;
	return false;
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::OnLButtonUp(UINT nFlags, CPoint point) 
{
	if (!m_pGraph)
		return;

	if(m_bIsFrozen)
		return;

	ReleaseCapture();

	bool bAltClick = CheckVirtualKey(VK_MENU);
	bool bCtrlClick = (nFlags & MK_CONTROL);
	bool bShiftClick = (nFlags & MK_SHIFT);

	switch (m_mode)
	{
	case SelectMode:
		{
			if (!m_rcSelect.IsRectEmpty())
			{
				bool bUnselect = bAltClick;

				CUndo undo("Select Graph Node(s)");
				bool isUndoRecorded = false;
				if (!bAltClick && !bCtrlClick && m_pGraph->IsSelectedAny())
				{
					m_pGraph->RecordUndo();
					isUndoRecorded = true;
					m_pGraph->UnselectAll();
				}

				std::multimap<int, CHyperNode*> nodes;
				GetNodesInRect( m_rcSelect,nodes,true );
				for (std::multimap<int, CHyperNode*>::reverse_iterator iter = nodes.rbegin(); iter!=nodes.rend(); ++iter)
				{
					CHyperNode *pNode = iter->second;
					IHyperGraphEnumerator* pEnum = pNode->GetRelatedNodesEnumerator();
					for (IHyperNode* pRelative = pEnum->GetFirst(); pRelative; pRelative = pEnum->GetNext())
					{
						if(!isUndoRecorded)
						{
							m_pGraph->RecordUndo();
							isUndoRecorded = true;
						}
						static_cast<CHyperNode*>(pRelative)->SetSelected(!bUnselect);
					}
				}
				OnSelectionChange();
				Invalidate();
			}
		}
		break;
	case MoveMode:
		m_moveHelper.clear();
		GetIEditor()->AcceptUndo( "Move Graph Node(s)" );
		SetScrollExtents();
		Invalidate();
		break;
	case EdgePointDragMode:
		{

			
		}
		break;
	case NodeBorderDragMode:
		{
			m_DraggingBorderNodeInfo.m_pNode = 0;
		} 
		break;
	case PortDragMode:
		{
			if (CHyperNode * pNode = (CHyperNode*) m_pGraph->FindNode( m_pDraggingEdge->nodeIn ))
			{
				pNode->UnselectAllPorts();
			}
			if (CHyperNode * pNode = (CHyperNode*) m_pGraph->FindNode( m_pDraggingEdge->nodeOut ))
			{
				pNode->UnselectAllPorts();
			}

			CHyperNode *pTrgNode = GetNodeAtPoint( point );
			if (pTrgNode)
			{
				int objectClicked = pTrgNode->GetObjectAt( GraphicsHelper(this), ViewToLocal(point) );
				bool specialDrag = false;
				if (objectClicked >= eSOID_ShiftFirstOutputPort && objectClicked <= eSOID_ShiftLastOutputPort)
				{
					if (bShiftClick)
						objectClicked = eSOID_FirstInputPort + (objectClicked - eSOID_ShiftFirstOutputPort);
					else if (bAltClick)
					{
						objectClicked = eSOID_FirstInputPort + (objectClicked - eSOID_ShiftFirstOutputPort);
						specialDrag = true;
					}
					else
						objectClicked = eSOID_Background;
				}
				CHyperNodePort *pTrgPort = NULL;
				if (objectClicked >= eSOID_FirstInputPort && objectClicked <= eSOID_LastInputPort && !pTrgNode->GetInputs()->empty())
					pTrgPort = &pTrgNode->GetInputs()->at(objectClicked - eSOID_FirstInputPort);
				else if (objectClicked >= eSOID_FirstOutputPort && objectClicked <= eSOID_LastOutputPort && !pTrgNode->GetOutputs()->empty())
					pTrgPort = &pTrgNode->GetOutputs()->at(objectClicked - eSOID_FirstOutputPort);
				else if (strcmp(pTrgNode->GetClassName(), CBlackBoxNode::GetClassType()) == 0)
				{
					pTrgPort = ((CBlackBoxNode*)(pTrgNode))->GetPortAtPoint(ViewToLocal(point));
					if(pTrgPort)
						pTrgNode = ((CBlackBoxNode*)(pTrgNode))->GetNode(pTrgPort);
				}

				if (pTrgPort)
				{
					CHyperNode * pSrcNode = 0;
					CHyperNodePort * pSrcPort = 0;
					if (m_bDraggingFixedOutput)
					{
						pSrcNode = (CHyperNode*) m_pGraph->FindNode( m_pDraggingEdge->nodeOut );
						pSrcPort = &pSrcNode->GetOutputs()->at( m_pDraggingEdge->nPortOut );
					}
					else
					{
						pSrcNode = (CHyperNode*) m_pGraph->FindNode( m_pDraggingEdge->nodeIn );
						pSrcPort = &pSrcNode->GetInputs()->at( m_pDraggingEdge->nPortIn );
						std::swap(pSrcNode, pTrgNode);
						std::swap(pSrcPort, pTrgPort);
					}
					if (m_pGraph->CanConnectPorts( pSrcNode,pSrcPort,pTrgNode,pTrgPort ))
					{
						// Connect
            m_pGraph->RecordUndo();
						m_pGraph->ConnectPorts( pSrcNode,pSrcPort,pTrgNode,pTrgPort,specialDrag );
						InvalidateNode( pSrcNode,true );
						InvalidateNode( pTrgNode,true );
						OnNodesConnected();
					}
				}
			}
			Invalidate();

			GetIEditor()->AcceptUndo( "Connect Graph Node" );
			m_pDraggingEdge = NULL;
		}
		break;
	}
	m_rcSelect.SetRect(0,0,0,0);

	if (m_mode == ScrollModeDrag)
	{
		Invalidate();
		GetIEditor()->AcceptUndo( "Connect Graph Node" );
		m_pDraggingEdge = NULL;
		m_mode = ScrollMode;
	}
	else
	{
		m_mode = NothingMode;
	}
	//CWnd::OnLButtonUp(nFlags, point);
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::OnRButtonDown(UINT nFlags, CPoint point) 
{
	if (!m_pGraph)
		return;

	HideEditPort();

	CHyperEdge *pHitEdge = 0;
	int nHitEdgePoint = 0;

	if (!m_bIsFrozen && HitTestEdge( point,pHitEdge,nHitEdgePoint ))
	{
		CMenu menu;
		menu.CreatePopupMenu();
		menu.AppendMenu(MF_STRING, 1, _T("Remove"));
		if (pHitEdge->IsEditable())
			menu.AppendMenu(MF_STRING, 2, _T("Edit"));
		if (m_pGraph->IsFlowGraph())
		{
			menu.AppendMenu(MF_STRING, 3, pHitEdge->enabled ? _T("Disable") : _T("Enable"));
			menu.AppendMenu(MF_STRING, 5, _T("Delay"));
		}

		// makes only sense for non-AIActions and while fg system is updated
		if (m_pGraph->IsFlowGraph() && m_pGraph->GetAIAction() == 0 && GetIEditor()->GetGameEngine()->IsFlowSystemUpdateEnabled())
			menu.AppendMenu(MF_STRING, 4, _T("Simulate flow [Double click]"));	

		CPoint pt;
		GetCursorPos(&pt);
		switch (menu.TrackPopupMenuEx(TPM_RETURNCMD, pt.x, pt.y, this, NULL))
		{
		case 1:
			{
				CUndo undo( "Remove Graph Edge" );
        m_pGraph->RecordUndo();
				m_pGraph->RemoveEdge( pHitEdge );
				OnEdgeRemoved();
			}
			break;
		case 2:
			{
				CUndo undo( "Edit Graph Edge" );
        m_pGraph->RecordUndo();
				m_pGraph->EditEdge( pHitEdge );
			}
			break;
		case 3:
			{
				CUndo undo ( pHitEdge->enabled ? "Disable Edge" : "Enable Edge");
        m_pGraph->RecordUndo();
				m_pGraph->EnableEdge(pHitEdge, !pHitEdge->enabled);
				InvalidateEdge(pHitEdge);
			}
			break;
		case 4:
			SimulateFlow(pHitEdge);
			break;
		case 5:
			{
				CUndo undo( "Add Delay" );
				CHyperNode* pDelayNode = CreateNode("Time:Delay", point);

				Gdiplus::RectF rect = pDelayNode->GetRect();
				Gdiplus::PointF adjustedPoint;

				adjustedPoint.X = rect.X - (rect.Width / 2.0f);
				adjustedPoint.Y = rect.Y;

				pDelayNode->SetPos(adjustedPoint);

				CHyperNodePort *pDelayOutputPort = &pDelayNode->GetOutputs()->at(0);
				CHyperNodePort *pDelayInputPort  = &pDelayNode->GetInputs()->at(0);

				CHyperNode *nodeIn = static_cast<CHyperNode*>(m_pGraph->FindNode(pHitEdge->nodeIn));
				CHyperNode *nodeOut = static_cast<CHyperNode*>(m_pGraph->FindNode(pHitEdge->nodeOut));

				if(pDelayInputPort && pDelayOutputPort && nodeIn && nodeOut)
				{
					m_pGraph->ConnectPorts(nodeOut, nodeOut->FindPort(pHitEdge->portOut, false), pDelayNode, pDelayInputPort, false);
					m_pGraph->ConnectPorts(pDelayNode, pDelayOutputPort, nodeIn, nodeIn->FindPort( pHitEdge->portIn,true ), false);
				}
			}					
			break;
		}
		return;
	}

	SetFocus();

	if (m_mode == PortDragMode)
	{
		m_mode = ScrollModeDrag;
	}
	else
	{
		if (m_mode == MoveMode)
			GetIEditor()->AcceptUndo( "Move Graph Node(s)" );

		m_mode = ScrollMode;
	}
	m_RButtonDownPos = point;
	m_mouseDownPos = point;

	SetCapture();

	__super::OnRButtonDown(nFlags, point);
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::OnRButtonUp(UINT nFlags, CPoint point) 
{
	if (!m_pGraph)
		return;

	ReleaseCapture();

	// make sure to reset the mode, otherwise we can end up in scrollmode while the context menu is open
	if (m_mode == ScrollModeDrag)
	{
		m_mode = PortDragMode;
	}
	else
	{
		m_mode = NothingMode;
	}

	if(m_bIsFrozen)
		return;

	if (abs(m_RButtonDownPos.x-point.x)<3 && abs(m_RButtonDownPos.y-point.y)<3)
	{
		// Show context menu if right clicked on the same point.
		CHyperNode *pNode = GetMouseSensibleNodeAtPoint( point );
		ShowContextMenu( point, pNode );
		return;
	}
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::OnMButtonDown(UINT nFlags, CPoint point) 
{
	__super::OnMButtonDown(nFlags, point);

	if (!m_pGraph)
		return;

	HideEditPort();
	SetFocus();
	m_mode = ScrollMode;
	m_RButtonDownPos = point;
	m_mouseDownPos = point;
	SetCapture();
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::OnMButtonUp(UINT nFlags, CPoint point) 
{
	__super::OnMButtonDown(nFlags, point);

	if (!m_pGraph)
		return;

	ReleaseCapture();
	m_mode = NothingMode;

	if((GetKeyState(VK_CONTROL) & 0x80) && m_pGraph->IsFlowGraph())
	{
		CFlowNode *pNode = static_cast<CFlowNode*>(GetMouseSensibleNodeAtPoint( point ));
		if (pNode)
		{
			CHyperNodePort* pPort = pNode->GetPortAtPoint(GraphicsHelper(this), ViewToLocal(point));

			if(pPort)
			{
				CFlowGraphDebuggerEditor* pFlowgraphDebugger = GetIEditor()->GetFlowGraphDebuggerEditor();

				if(pFlowgraphDebugger)
				{
					const bool hasBreakPoint = pFlowgraphDebugger->HasBreakpoint(pNode, pPort);
					const bool invalidateNode = hasBreakPoint || (pPort->nConnected || !pPort->bInput);

					if (hasBreakPoint)
					{
						pFlowgraphDebugger->RemoveBreakpoint(pNode, pPort);
					}
					// if the input is not connected at all with an edge, don't add a breakpoint
					else if(pPort->nConnected || !pPort->bInput)
					{
						pFlowgraphDebugger->AddBreakpoint(pNode, pPort);
					}

					// redraw the node to update the "visual" breakpoint, otherwise you have to move the mouse first
					// to trigger a redraw
					if(invalidateNode)
						InvalidateNode(pNode, true, false);
				}	
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::OnMouseMove(UINT nFlags, CPoint point)
{
	if (!m_pGraph)
		return;

	if (m_pMouseOverNode)
		m_pMouseOverNode->UnselectAllPorts();

	m_pMouseOverNode = NULL;
	m_pMouseOverPort = NULL;
	
	switch (m_mode)
	{
	case MoveMode:
		{
			GetIEditor()->RestoreUndo();
			CPoint offset = point - m_mouseDownPos;
			MoveSelectedNodes( offset );
		}
		break;
	case ScrollMode:
		OnMouseMoveScrollMode(nFlags, point);
		break;
	case SelectMode:
		{
			// Update select rectangle.
			CRect rc( m_mouseDownPos.x,m_mouseDownPos.y,point.x,point.y );
			rc.NormalizeRect();
			CRect rcClient;
			GetClientRect(rcClient);
			rc.IntersectRect(rc,rcClient);

			CDC *dc = GetDC();
			dc->DrawDragRect( rc,CSize(1,1),m_rcSelect,CSize(1,1) );
			ReleaseDC(dc);
			m_rcSelect = rc;
		}
		break;
	case PortDragMode:
		OnMouseMovePortDragMode(nFlags, point);
		break;
	case NodeBorderDragMode:
		OnMouseMoveBorderDragMode( nFlags, point );
		break;		
	case EdgePointDragMode:
		if (m_pHitEdge)
		{
			if (m_bSplineArrows)
			{
				Gdiplus::PointF p1 = ViewToLocal(point);
				m_pHitEdge->cornerPoints[0] = p1;
				m_pHitEdge->cornerModified = true;
			}
			else
			{
				Gdiplus::PointF p1 = ViewToLocal(point);
				Gdiplus::PointF p2 = ViewToLocal(m_mouseDownPos);
				if (m_nHitEdgePoint < 2)
				{
					float w = p1.X - p2.X;
					m_pHitEdge->cornerW = m_prevCornerW + w;
					m_pHitEdge->cornerModified = true;
				}
				else
				{
					float h = p1.Y - p2.Y;
					m_pHitEdge->cornerH = m_prevCornerH + h;
					m_pHitEdge->cornerModified = true;
				}
			}
			Invalidate();
		}
		break;
	case ScrollModeDrag:
		OnMouseMoveScrollMode(nFlags, point);
		OnMouseMovePortDragMode(nFlags, point);
		break;
	default:
		{
			CHyperNodePort* pPort = NULL;
			CHyperNode* pNode = GetNodeAtPoint(point);
		
			Invalidate();

			if (pNode)
			{
				m_pMouseOverNode = pNode;
				pPort = pNode->GetPortAtPoint( GraphicsHelper(this), ViewToLocal(point) );

				if (pPort)
				{
					pPort->bSelected = true;
				}

				m_pMouseOverPort = pPort;
			}

			if(prevMovePoint != point)
			{
				UpdateTooltip(pNode,pPort);
				prevMovePoint=point;
			}
		}
	}

	__super::OnMouseMove(nFlags, point);
}


//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::OnMouseMoveScrollMode(UINT nFlags, CPoint point)
{
	if (!((abs(m_RButtonDownPos.x-point.x)<3 && abs(m_RButtonDownPos.y-point.y)<3)))
	{
		CPoint offset = point - m_mouseDownPos;
		m_scrollOffset += offset;
		m_mouseDownPos = point;
		SetScrollExtents(false);
		Invalidate(FALSE);
	}
}


//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::OnMouseMoveBorderDragMode( UINT nFlags, CPoint point )
{
	if (m_DraggingBorderNodeInfo.m_pNode)
	{
		Gdiplus::PointF mousePt = ViewToLocal(point);
		Gdiplus::PointF moved = mousePt - m_DraggingBorderNodeInfo.m_clickPoint;
		
		if (moved.Y==0 && moved.X==0)
			return;
		
		bool bUp = false;
		bool bDown = false;
		bool bLeft = false;
		bool bRight = false;
		
		Gdiplus::PointF nodePos = m_DraggingBorderNodeInfo.m_origNodePos;
		Gdiplus::RectF borderRect = m_DraggingBorderNodeInfo.m_origBorderRect;
		
		switch (m_DraggingBorderNodeInfo.m_border)
		{
			case eSOID_Border_UpRight:
				bUp = true;
				bRight = true;
				break;
				
			case eSOID_Border_Right:
				bRight = true;
				break;
				
			case eSOID_Border_DownRight:
				bDown = true;
				bRight = true;
				break;
				
			case eSOID_Border_Down:
				bDown = true;
				break;
				
			case eSOID_Border_DownLeft:
				bDown = true;
				bLeft = true;
				break;
				
			case eSOID_Border_Left:
				bLeft = true;
				break;
				
			case eSOID_Border_UpLeft:
				bUp = true;
				bLeft = true;
				break;
				
			case eSOID_Border_Up:
				bUp = true;
				break;
		}
		
		if (bUp)
		{
			float inc = - moved.Y;
			nodePos.Y -= inc;
			borderRect.Height += inc;
		}
		
		if (bDown)
		{
			float inc = moved.Y;
			borderRect.Height += inc;
		}

		if (bRight)
		{
			float inc = moved.X;
			borderRect.Width += inc;
		}

		if (bLeft)
		{
			float inc = - moved.X;
			nodePos.X -= inc;
			borderRect.Width += inc;
		}
		
		if (borderRect.Width<10 || borderRect.Height<10) 
			return;
		
		if (bLeft || bUp)
			m_DraggingBorderNodeInfo.m_pNode->SetPos( nodePos );
		m_DraggingBorderNodeInfo.m_pNode->SetResizeBorderRect( borderRect );
		m_DraggingBorderNodeInfo.m_pNode->Invalidate( true );
		ForceRedraw();
	}				
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::OnMouseMovePortDragMode(UINT nFlags, CPoint point)
{
	InvalidateEdgeRect( LocalToView(m_pDraggingEdge->pointOut), LocalToView(m_pDraggingEdge->pointIn) );
	/*
	if (CHyperNode * pNode = (CHyperNode*) m_pGraph->FindNode( m_pDraggingEdge->nodeIn ))
	{
	pNode->GetInputs()->at(m_pDraggingEdge->nPortIn).bSelected = false;
	InvalidateNode( pNode );
	}
	if (CHyperNode * pNode = (CHyperNode*) m_pGraph->FindNode( m_pDraggingEdge->nodeOut ))
	InvalidateNode( pNode );
	*/

	CHyperNode *pNodeIn = (CHyperNode*) m_pGraph->FindNode( m_pDraggingEdge->nodeIn );
	CHyperNode *pNodeOut = (CHyperNode*) m_pGraph->FindNode( m_pDraggingEdge->nodeOut );
	if (pNodeIn)
	{
		CHyperNode::Ports *ports = pNodeIn->GetInputs();
		if(ports->size() > m_pDraggingEdge->nPortIn)
			ports->at(m_pDraggingEdge->nPortIn).bSelected = false;
	}

	if (m_bDraggingFixedOutput)
	{
		m_pDraggingEdge->pointIn = ViewToLocal(point);
		m_pDraggingEdge->nodeIn = -1;
	}
	else
	{
		m_pDraggingEdge->pointOut = ViewToLocal(point);
		m_pDraggingEdge->nodeOut = -1;
	}
	CHyperNode* pNode = GetNodeAtPoint( point );
	if (pNode)
	{
		if (CHyperNodePort * pPort = pNode->GetPortAtPoint( GraphicsHelper(this), m_pDraggingEdge->pointIn ))
		{
			if (m_bDraggingFixedOutput && pPort->bInput)
			{
				m_pDraggingEdge->nodeIn = pNode->GetId();
				m_pDraggingEdge->nPortIn = pPort->nPortIndex;
				pNode->GetInputs()->at(m_pDraggingEdge->nPortIn).bSelected = true;
			}
			else if (!m_bDraggingFixedOutput && !pPort->bInput)
			{
				m_pDraggingEdge->nodeOut = pNode->GetId();
				m_pDraggingEdge->nPortOut = pPort->nPortIndex;
				pNode->GetOutputs()->at(m_pDraggingEdge->nPortOut).bSelected = true;
			}
		}
	}
	m_edgesToReroute.insert(m_pDraggingEdge);

	if (pNode)
		InvalidateNode(pNode,true);
	if (pNodeIn)
		InvalidateNode(pNodeIn,true);
	if (pNodeOut)
	{
		if (m_pDraggingEdge->nPortOut >= 0 && m_pDraggingEdge->nPortOut < pNodeOut->GetOutputs()->size())
			pNodeOut->GetOutputs()->at(m_pDraggingEdge->nPortOut).bSelected = true;
		InvalidateNode(pNodeOut,true);
	}
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::UpdateTooltip( CHyperNode *pNode,CHyperNodePort *pPort )
{
	if (m_tooltip.m_hWnd)
	{
		if (pNode && pNode->IsTooltipShowable() && gSettings.bFlowGraphShowToolTip)
		{
			CString tip;
			if (pPort)
			{
				// Port tooltip.
				CString type;
				switch (pPort->pVar->GetType())
				{
				case IVariable::INT: type = "Integer"; break;
				case IVariable::FLOAT: type = "Float"; break;
				case IVariable::BOOL: type = "Boolean"; break;
				case IVariable::VECTOR: type = "Vector"; break;
				case IVariable::STRING: type = "String"; break;
				//case IVariable::VOID: type = "Void"; break;
				default:
					type = "Any"; break;
				}
				const char *desc = pPort->pVar->GetDescription();
				if (desc && *desc)
					tip.Format( "[%s] %s",(const char*)type,desc );
				else
					tip.Format( "[%s] %s",(const char*)type,(const char*)pNode->GetDescription() );
			}
			else
			{
				// Node tooltip.
				if (pNode->IsEditorSpecialNode())
				{
					tip.Format( "Name: %s\nClass: %s\n%s",
						pNode->GetName(),pNode->GetClassName(),pNode->GetDescription() );
				}
				else
				{	
					CFlowNode *pFlowNode = static_cast<CFlowNode*> (pNode);
					CString cat = pFlowNode->GetCategoryName();
					const uint32 usageFlags = pFlowNode->GetUsageFlags();
					// TODO: something with Usage flags
					tip.Format( "Name: %s\nClass: %s\nCategory: %s\nDescription: %s",
						pFlowNode->GetName(),pFlowNode->GetClassName(),cat.GetString(),pFlowNode->GetDescription() );
				}
			}
			//CString oldtip;
			//m_tooltip.GetText( oldtip,this,1 );
			//if (oldtip != tip)
			{
				m_tooltip.UpdateTipText( tip,this,1 );
				m_tooltip.Activate(TRUE);
			}
			//else
				//m_tooltip.Activate(FALSE);
		}
		else
		{
			m_tooltip.Activate(FALSE);
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::OnLButtonDblClk(UINT nFlags, CPoint point) 
{
	__super::OnLButtonDblClk(nFlags, point);

	if (m_pGraph == 0)
		return;

	if(m_bIsFrozen)
		return;

	SW_TEST_GRAPH_MOD(m_pGraph, true)

	CHyperEdge *pHitEdge = 0;
	int nHitEdgePoint = 0;
	if (HitTestEdge( point,pHitEdge,nHitEdgePoint ))
	{
		// makes only sense for non-AIActions and while fg system is updated
		if (m_pGraph->IsFlowGraph() && m_pGraph->GetAIAction() == 0 && GetIEditor()->GetGameEngine()->IsFlowSystemUpdateEnabled())
		{
			SimulateFlow(pHitEdge);
		}
	}
	else
	{
		CHyperNode *pNode = GetMouseSensibleNodeAtPoint( point );
		if (pNode && pNode->IsEditorSpecialNode())
		{
			if(!strcmp(pNode->GetClassName(), "QuickSearch"))
				QuickSearchNode(pNode);
			else
				RenameNode(pNode);
		}
		else
		{
			if(m_pEditPort)
				ShowEditPort(pNode, m_pEditPort);
			else
			{
				//int objectClicked = pNode->GetObjectAt( GraphicsHelper(this), ViewToLocal(point) );
				if (m_pGraph->IsFlowGraph())
					OnSelectEntity();
			}
		}
	}
}


//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
	bool processed = true;
	switch (nChar)
	{
	case 'F':
		m_bHighlightIncomingEdges = true;
		InvalidateView();
		break;
	case 'G':
		m_bHighlightOutgoingEdges = true;
		InvalidateView();
		break;
	case 'O':
		SetZoom( m_zoom + 0.3f );
		InvalidateView();
		break;
	case 'P':
		SetZoom( m_zoom - 0.3f );
		InvalidateView();
		break;
	case 'Q':
		{
			if(!m_pQuickSearchNode)
			{
				CPoint point;
				GetCursorPos(&point);
				ScreenToClient(&point);
				m_pQuickSearchNode = CreateNode( CQuickSearchNode::GetClassType(), point );
				if (m_pQuickSearchNode)
					QuickSearchNode( m_pQuickSearchNode );
			}

			InvalidateView();
		}
		break;
	case VK_F5:
		GetIEditor()->GetFlowGraphDebuggerEditor()->ResumeGame();
		break;
	default:
		__super::OnKeyDown(nChar, nRepCnt, nFlags);
	}
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
	bool processed = false;
	if (nChar == 'F') {
		processed = true;
		m_bHighlightIncomingEdges = false;
		InvalidateView();
	} else if (nChar == 'G') {
		processed = true;
		m_bHighlightOutgoingEdges = false;
		InvalidateView();
	} else if (nChar == 'R') {
		processed = true;
		ForceRedraw();
	}
	if (!processed)
		__super::OnKeyUp(nChar, nRepCnt, nFlags);
}

//////////////////////////////////////////////////////////////////////////perforce
BOOL CHyperGraphView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
	if( m_mode == NothingMode )
	{
		CPoint point;
		GetCursorPos(&point);
		ScreenToClient(&point);

		CHyperEdge *pHitEdge = 0;
		int nHitEdgePoint = 0;
		if (HitTestEdge( point,pHitEdge,nHitEdgePoint ))
		{
			HCURSOR hCursor;
			hCursor = AfxGetApp()->LoadCursor(IDC_ARRWHITE);
			SetCursor(hCursor);
			return TRUE;
		}
		
		CHyperNode *pNode = GetNodeAtPoint( point );
		if (pNode)
		{
			int object = pNode->GetObjectAt( GraphicsHelper(this), ViewToLocal(point) );
			LPCTSTR resource = NULL;
			switch (object)
			{
				case eSOID_Border_UpRight:
				case eSOID_Border_DownLeft:
					resource = IDC_SIZENESW;
					break;
				
				case eSOID_Border_Right:
				case eSOID_Border_Left:
					resource = IDC_SIZEWE;
					break;
				
				case eSOID_Border_DownRight:
				case eSOID_Border_UpLeft:
					resource = IDC_SIZENWSE;
					break;
					
				case eSOID_Border_Up:
				case eSOID_Border_Down:
					resource = IDC_SIZENS;
					break;
			}
			if (resource)
			{
				HCURSOR hCursor;
				hCursor = AfxGetApp()->LoadStandardCursor( resource );
				SetCursor(hCursor);
				return TRUE;
			}
		}
	}

	return __super::OnSetCursor(pWnd, nHitTest, message);
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::InvalidateEdgeRect( CPoint p1,CPoint p2 )
{
	CRect rc(p1,p2);
	rc.NormalizeRect();
	rc.InflateRect(20,7,20,7);
	InvalidateRect(rc);
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::InvalidateEdge( CHyperEdge *pEdge )
{
	m_edgesToReroute.insert(pEdge);
	InvalidateEdgeRect( LocalToView(pEdge->pointIn),LocalToView(pEdge->pointOut) );
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::InvalidateNode( CHyperNode *pNode,bool bRedraw, bool bIsModified/*=true*/ )
{
	assert( pNode );
	if (!m_pGraph)
		return;

	CDC* pDC = GetDC();
	if (pDC)
	{
		if (pNode->IsEditorSpecialNode())
			InvalidateRect( LocalToViewRect( pNode->GetRect() ), FALSE );
		Gdiplus::Graphics gr(pDC->GetSafeHdc());
		Gdiplus::RectF rc = pNode->GetRect();
		Gdiplus::SizeF sz = pNode->CalculateSize(gr);
		rc.Width = sz.Width;
		rc.Height = sz.Height;
		ReleaseDC(pDC);

		pNode->SetRect(rc);
		rc.Inflate(GRID_SIZE, GRID_SIZE);
		InvalidateRect( LocalToViewRect(rc),FALSE );

		if (bRedraw)
			pNode->Invalidate(true, bIsModified);

		// Invalidate all edges connected to this node.
		std::vector<CHyperEdge*> edges;
		if (m_pGraph->FindEdges( pNode,edges ))
		{
			// Invalidate all node edges.
			for (int i = 0; i < edges.size(); i++)
			{
				InvalidateEdge( edges[i] );
			}
		}
	}
	else
	{
		assert(false);
	}
}

void CHyperGraphView::UpdateDebugCount(CHyperNode* pNode)
{
	if (!m_pGraph || !pNode)
		return;

	std::vector<CHyperEdge*> edges;
	int count = pNode->GetDebugCount();

	if (m_pGraph->FindEdges(pNode, edges))
	{
		for (int i = 0; i < edges.size(); ++i)
		{
			CHyperNode *nodeIn = static_cast<CHyperNode*>(m_pGraph->FindNode(edges[i]->nodeIn));
			if (!nodeIn)
				continue;

			CHyperNodePort *portIn = nodeIn->FindPort(edges[i]->portIn, true);
			if (!portIn)
				continue;

			if (nodeIn->IsDebugPortActivated(portIn))
			{
				edges[i]->debugCount++;
				nodeIn->ResetDebugPortActivation(portIn);
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::DrawEdges( Gdiplus::Graphics &gr,const CRect &rc )
{
		FUNCTION_PROFILER(gEnv->pSystem, PROFILE_EDITOR);

		std::vector<CHyperEdge*> edges;
		m_pGraph->GetAllEdges( edges );

		CHyperEdge *pHitEdge = 0;
		int nHitEdgePoint = 0;

		CPoint cursorPos;
		GetCursorPos(&cursorPos);
		ScreenToClient(&cursorPos);
		HitTestEdge(cursorPos, pHitEdge, nHitEdgePoint);
								
		for (std::vector<CHyperEdge*>::const_iterator it = edges.begin(), end = edges.end(); it != end; ++it)
		{
				CHyperEdge* pEdge = *it;

				CHyperNode *nodeIn = (CHyperNode*)m_pGraph->FindNode(pEdge->nodeIn);
				CHyperNode *nodeOut = (CHyperNode*)m_pGraph->FindNode(pEdge->nodeOut);
				if (!nodeIn || !nodeOut)
						continue;
				CHyperNodePort *portIn = nodeIn->FindPort( pEdge->portIn,true );
				CHyperNodePort *portOut = nodeOut->FindPort( pEdge->portOut,false );
				if (!portIn || !portOut)
						continue;

				// Draw arrow.
				int selection = 0;
				selection |= (m_bHighlightIncomingEdges && nodeIn->IsSelected())  /* << 0 */ ;
				selection |= (m_bHighlightOutgoingEdges && nodeOut->IsSelected()) << 1;
				if(selection == 0)
						selection = (nodeIn->IsPortActivationModified(portIn)) ? 4 : 0;

				if (m_pMouseOverPort == portIn || m_pMouseOverPort == portOut || pHitEdge == pEdge)
				{
					selection = 8;
				} 

				// Check for custom selection mode here
				int customSelMode = pEdge->GetCustomSelectionMode();
				if (selection == 0 && customSelMode != -1)
						selection = customSelMode;

				if(nodeIn->GetBlackBox() || nodeOut->GetBlackBox())
				{
						if(nodeIn->GetBlackBox() && nodeOut->GetBlackBox())
								continue;

						if(nodeIn->GetBlackBox())
						{
								pEdge->pointIn = ((CBlackBoxNode*)(nodeIn->GetBlackBox()))->GetPointForPort(portIn);
						}
						else
						{
								pEdge->pointOut = ((CBlackBoxNode*)(nodeOut->GetBlackBox()))->GetPointForPort(portOut);
						}
						Gdiplus::RectF box = DrawArrow( gr,pEdge,true,selection);
				}
				else
				{
						Gdiplus::RectF box = DrawArrow( gr,pEdge,true,selection);
						pEdge->DrawSpecial( &gr, Gdiplus::PointF(box.X + box.Width/2, box.Y + box.Height/2) );
				}
		}
}
//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::CenterViewAroundNode(CHyperNode* poNode,bool boFitZoom)
{
	CRect rc;
	GetClientRect(rc);

	m_scrollOffset.SetPoint(0,0);

	CRect rcGraph = LocalToViewRect(poNode->GetRect());

	if (boFitZoom)
	{
		float zoom = (float)max(rc.Width(),rc.Height()) / (max(rcGraph.Width(),rcGraph.Height()) + GRID_SIZE*4);
		if (zoom < 1)
		{
			SetZoom( zoom );
		}
	}

	rcGraph = LocalToViewRect(poNode->GetRect());
	if (rcGraph.Width() < rc.Width())
	{
		m_scrollOffset.x += (rc.Width() - rcGraph.Width()) / 2;
	}
	if (rcGraph.Height() < rc.Height())
	{
		m_scrollOffset.y += (rc.Height() - rcGraph.Height()) / 2;
	}

	m_scrollOffset.x += -rcGraph.left + GRID_SIZE;
	m_scrollOffset.y += -rcGraph.top + GRID_SIZE;

	InvalidateView();
	Invalidate();
}


//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::DrawNodes( Gdiplus::Graphics& gr,const CRect &rc )
{
  FUNCTION_PROFILER(gEnv->pSystem, PROFILE_EDITOR);

	std::multimap<int, CHyperNode*> nodes;
	GetNodesInRect( rc,nodes );

	std::vector<CHyperNode*> selectedNodes;
	const bool bNodesSelected = GetSelectedNodes(selectedNodes);

	for (std::multimap<int, CHyperNode*>::iterator iter = nodes.begin(); iter!=nodes.end(); ++iter)
	{
		CHyperNode *pNode = iter->second;

		if ((false == bNodesSelected) || (false == stl::find(selectedNodes, pNode)))
		{
				pNode->Draw( this,gr,true );
		}
	}

	if(bNodesSelected)
	{
			for (std::vector<CHyperNode*>::const_iterator iter = selectedNodes.begin(); iter!=selectedNodes.end();++iter)
			{
					(*iter)->Draw( this,gr,true );
			}
	}
}

//////////////////////////////////////////////////////////////////////////
CRect CHyperGraphView::LocalToViewRect( const Gdiplus::RectF &localRect ) const
{
	Gdiplus::RectF temp = localRect;
	temp.X *= m_zoom;
	temp.Y *= m_zoom;
	temp.Width *= m_zoom;
	temp.Height *= m_zoom;
	temp.Offset( Gdiplus::PointF(m_scrollOffset.x, m_scrollOffset.y) );
	return CRect(temp.X-1.0f, temp.Y-1.0f, temp.GetRight()+1.05f, temp.GetBottom()+1.0f);
}

//////////////////////////////////////////////////////////////////////////
Gdiplus::RectF CHyperGraphView::ViewToLocalRect( const CRect &viewRect ) const
{
	Gdiplus::RectF rc( viewRect.left, viewRect.top, viewRect.Width(), viewRect.Height() );
	rc.Offset( -m_scrollOffset.x, -m_scrollOffset.y );
	rc.X /= m_zoom;
	rc.Y /= m_zoom;
	rc.Width /= m_zoom;
	rc.Height /= m_zoom;
	return rc;
}

//////////////////////////////////////////////////////////////////////////
CPoint CHyperGraphView::LocalToView( Gdiplus::PointF point )
{
	return CPoint(point.X*m_zoom+m_scrollOffset.x,point.Y*m_zoom+m_scrollOffset.y);
}

//////////////////////////////////////////////////////////////////////////
Gdiplus::PointF CHyperGraphView::ViewToLocal( CPoint point )
{
	return Gdiplus::PointF((point.x-m_scrollOffset.x)/m_zoom,(point.y-m_scrollOffset.y)/m_zoom);
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::OnHyperGraphEvent( IHyperNode *pINode,EHyperGraphEvent event )
{
	CHyperNode* pNode = static_cast<CHyperNode*>(pINode);
	switch (event)
	{
	case EHG_GRAPH_REMOVED:
		if (!m_pGraph->GetManager())
			SetGraph( NULL );
		break;
	case EHG_GRAPH_INVALIDATE:
		m_bHighlightOutgoingEdges = false;
		m_bHighlightIncomingEdges = false;
		m_edgesToReroute.clear();
		m_pEditedNode = 0;
		m_pMultiEditedNodes.resize(0);
		m_pMouseOverNode = NULL;
		m_pMouseOverPort = NULL;
		m_DraggingBorderNodeInfo.m_pNode = 0;
		m_moveHelper.clear();
		InvalidateView();
		OnSelectionChange();
		break;
	case EHG_NODE_ADD:
		InvalidateView();
		break;
	case EHG_NODE_DELETE:
		if (pINode == static_cast<CHyperNode*>(m_pMouseOverNode))
		{
			m_pMouseOverNode = NULL;
			m_pMouseOverPort = NULL;
		}
		InvalidateView();
		break;
	case EHG_NODE_CHANGE:
		{
			InvalidateNode(pNode);
		}
		break;
	case EHG_NODE_CHANGE_DEBUG_PORT:
		InvalidateNode(pNode, true);
		if(m_pGraph && m_pGraph->FindNode(pNode->GetId()))
		{
			UpdateDebugCount(pNode);
		}
		break;
	}
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::SetGraph( CHyperGraph *pGraph )
{
	HideEditPort();
	if (m_pGraph == pGraph)
		return;

	m_bHighlightOutgoingEdges = false;
	m_bHighlightIncomingEdges = false;
	m_edgesToReroute.clear();
	m_pEditedNode = 0;
	m_pMultiEditedNodes.resize(0);
	m_pMouseOverNode = NULL;
	m_pMouseOverPort = NULL;
	m_DraggingBorderNodeInfo.m_pNode = 0;
	m_moveHelper.clear();

	if (m_pGraph)
	{
		// CryLogAlways("CHyperGraphView: (1) Removing as listener from 0x%p before switching to 0x%p", m_pGraph, pGraph);
		m_pGraph->RemoveListener(this);
		// CryLogAlways("CHyperGraphView: (2) Removing as listener from 0x%p before switching to 0x%p", m_pGraph, pGraph);
	}

	bool bFitAll = true;

	m_pGraph = pGraph;
	if (m_pGraph)
	{
		if (m_pGraph->GetViewPosition( m_scrollOffset,m_zoom ))
		{
			bFitAll = false;
			InvalidateView();
		}

		RerouteAllEdges();
		m_pGraph->AddListener( this );
		NotifyZoomChangeToNodes();
	}

	// Invalidate all view.
	if (m_hWnd && bFitAll)
	{
		OnCommandFitAll();
	}
	if (m_hWnd) {
		OnSelectionChange();
	}

	UpdateFrozen();

	if(m_pGraph && m_pGraph->IsNodeActivationModified())
		OnPaint();
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::OnCommandFitAll()
{
	CRect rc;
	GetClientRect(rc);

	if (rc.IsRectEmpty())
	{
		m_bRefitGraphInOnSize = true;
		return;
	}

	SetZoom(1);
	m_scrollOffset.SetPoint(0,0);
	UpdateWorkingArea();
	CRect rcGraph = LocalToViewRect(m_workingArea);

	float zoom = min(float(rc.Width()) / rcGraph.Width(), float(rc.Height()) / rcGraph.Height());
	if(zoom < 1)
	{
		SetZoom( zoom );
		rcGraph = LocalToViewRect(m_workingArea); // used new zoom
	}

	if (rcGraph.Width() < rc.Width())
		m_scrollOffset.x += (rc.Width() - rcGraph.Width()) / 2;
	if (rcGraph.Height() < rc.Height())
		m_scrollOffset.y += (rc.Height() - rcGraph.Height()) / 2;

	m_scrollOffset.x -= rcGraph.left;
	m_scrollOffset.y -= rcGraph.top;

	InvalidateView();
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::ShowAndSelectNode(CHyperNode* pToNode, bool bSelect)
{
	assert (pToNode != 0);

	if (!m_pGraph)
		return;

	CRect rc;
	GetClientRect(rc);

	CRect nodeRect = LocalToViewRect(pToNode->GetRect());

	//// check if fully inside
	//if (nodeRect.left >= rc.left &&
	//	nodeRect.top >= rc.top &&
	//	nodeRect.bottom <= rc.bottom &&
	//	nodeRect.right <= rc.right)
	//{
	//	// fully inside
	//	// do nothing yet...
	//}
	//else
	{
		if (rc.IsRectEmpty())
		{
			m_bRefitGraphInOnSize = true;
		}
		else
		{
			m_scrollOffset.SetPoint(0,0);

			Gdiplus::RectF	stRect=pToNode->GetRect();

			if(stRect.IsEmptyArea())
			{
				CDC* pDC = GetDC();
				if (pDC)
				{
					Gdiplus::Graphics gr(pDC->GetSafeHdc());
					pToNode->CalculateSize(gr);
					stRect=pToNode->GetRect();
					ReleaseDC(pDC);
				}
				else
				{
					assert(false);
				}
			}

			CRect rcGraph;

			rcGraph.left=stRect.X;
			rcGraph.right=stRect.X+stRect.Width;

			rcGraph.top=stRect.Y;
			rcGraph.bottom=stRect.Y+stRect.Height;

			int maxWH = max(rcGraph.Width(),rcGraph.Height());
			if(maxWH>0)
			{
				float fZoom=(float)(max(rc.Width(),rc.Height()))*0.33f / maxWH;
				SetZoom( fZoom );
			}
			else
			{
				SetZoom( 1.0f );
			}

			rcGraph = LocalToViewRect(pToNode->GetRect());

			if (rcGraph.Width() < rc.Width())
				m_scrollOffset.x += (rc.Width() - rcGraph.Width()) / 2;
			if (rcGraph.Height() < rc.Height())
				m_scrollOffset.y += (rc.Height() - rcGraph.Height()) / 2;

			m_scrollOffset.x += -rcGraph.left + GRID_SIZE;
			m_scrollOffset.y += -rcGraph.top + GRID_SIZE;

			//m_scrollOffset.SetPoint(0,0);
			//CRect rcGraph = LocalToViewRect(pToNode->GetRect());
			//float zoom = (float)max(rc.Width(),rc.Height()) / (max(rcGraph.Width(),rcGraph.Height()) + GRID_SIZE*4);
			//if (zoom < 1)
			//	m_zoom = CLAMP( zoom, MIN_ZOOM, MAX_ZOOM ); 
			//rcGraph = LocalToViewRect(pToNode->GetRect());

			//if (rcGraph.Width() < rc.Width())
			//	m_scrollOffset.x += (rc.Width() - rcGraph.Width()) / 2;
			//if (rcGraph.Height() < rc.Height())
			//	m_scrollOffset.y += (rc.Height() - rcGraph.Height()) / 2;

			//m_scrollOffset.x += -rcGraph.left + GRID_SIZE;
			//m_scrollOffset.y += -rcGraph.top + GRID_SIZE;
		}
	}

	if (bSelect)
	{
		IHyperGraphEnumerator *pEnum = m_pGraph->GetNodesEnumerator();
		for (IHyperNode *pINode = pEnum->GetFirst(); pINode; pINode = pEnum->GetNext())
		{
			CHyperNode *pNode = (CHyperNode*)pINode;
			if (pNode->IsSelected())
				pNode->SetSelected(false);
		}
		pToNode->SetSelected(true);

		OnSelectionChange();
		pEnum->Release();
	}
	InvalidateView();
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::DrawGrid( Gdiplus::Graphics &gr,const CRect &updateRect )
{
  FUNCTION_PROFILER(gEnv->pSystem, PROFILE_EDITOR);

	int gridStep = 10;

	float z = m_zoom;
	assert (z >= MIN_ZOOM);
	while (z < 0.99f)
	{
		z *= 2;
		gridStep *= 2;
	}

	// Draw grid line every 5 pixels.	
	Gdiplus::RectF updLocalRect = ViewToLocalRect(updateRect);
	float startX = updLocalRect.X - fmodf(updLocalRect.X, gridStep);
	float startY = updLocalRect.Y - fmodf(updLocalRect.Y, gridStep);
	float stopX = startX + updLocalRect.Width;
	float stopY = startY + updLocalRect.Height;

	Gdiplus::Pen gridPen( GRID_COLOR, 1.0f );

	// Draw vertical grid lines.
	for (float x = startX; x < stopX; x += gridStep)
		gr.DrawLine( &gridPen, Gdiplus::PointF(x,startY), Gdiplus::PointF(x,stopY) );

	// Draw horizontal grid lines.
	for (float y = startY; y < stopY; y += gridStep)
		gr.DrawLine( &gridPen, Gdiplus::PointF(startX,y), Gdiplus::PointF(stopX,y) );
}

//////////////////////////////////////////////////////////////////////////
Gdiplus::RectF CHyperGraphView::DrawArrow( Gdiplus::Graphics &gr,CHyperEdge *pEdge, bool helper, int selection)
{
  FUNCTION_PROFILER(gEnv->pSystem, PROFILE_EDITOR);

	// optimization: don't draw helper or label text when zoomed out
	static const float fHelperDrawMinimumZoom = 0.4f;
	Gdiplus::PointF pout = pEdge->pointOut;
	Gdiplus::PointF pin = pEdge->pointIn;
	EHyperEdgeDirection dout = pEdge->dirOut;
	EHyperEdgeDirection din = pEdge->dirIn;

	struct 
	{
		float x1, y1;
		float x2, y2;
	}
	h[] =
	{
		{0,-0.5f,0,-0.5f},
		{0,0.5f,0,0.5f},
		{-0.5f,0,-0.5f,0},
		{0.5f,0,0.5f,0}
	};

	float dx = CLAMP(fabsf(pout.X - pin.X), 20.0f, 150.0f);
	float dy = CLAMP(fabsf(pout.Y - pin.Y), 20.0f, 150.0f);

	if (fabsf(pout.X - pin.X) < 0.0001f && fabsf(pout.Y - pin.Y) < 0.0001f)
		return Gdiplus::RectF(0,0,0,0);

	Gdiplus::PointF pnts[6];

	pnts[0] = Gdiplus::PointF(pout.X,pout.Y);
	pnts[1] = Gdiplus::PointF(pnts[0].X+h[dout].x1*dx,pnts[0].Y+h[dout].y1*dy);
	pnts[3] = Gdiplus::PointF(pin.X,pin.Y);
	pnts[2] = Gdiplus::PointF(pnts[3].X+h[din].x2*dx,pnts[3].Y+h[din].y2*dy);
	Gdiplus::PointF center = Gdiplus::PointF((pnts[1].X+pnts[2].X)*0.5f, (pnts[1].Y+pnts[2].Y)*0.5f);

	float zoom = m_zoom;
	if (zoom > 1.0f)
		zoom = 1.0f;

	Gdiplus::Color color;
	Gdiplus::Pen pen( color );
	
	switch (selection)
	{
	case 1: // incoming
		color = ARROW_SEL_COLOR_IN;
		break;
	case 2: // outgoing
		color = ARROW_SEL_COLOR_OUT;
		break;
	case 3: // incoming+outgoing
		color = Gdiplus::Color(244,244,244);
		break;
	case 4: // debugging port activation
		color = PORT_DEBUGGING_COLOR;
		break;
	case 5: // AG Modifier Link Color
		color = Gdiplus::Color(44,44,90);
		pen.SetDashStyle(Gdiplus::DashStyleDot);
		pen.SetWidth(1.5f);
		break;
	case 6: // AG Regular Link Color
		color = Gdiplus::Color(40,40,44);
		break;
	case 7: // Selection Tree Link Color
		color = Gdiplus::Color( 0, 0, 0 );
		pen.SetWidth( 1.5f );
		break;
	case 8:
		color = PORT_EDGE_HIGHLIGHT;
		pen.SetWidth( 1.5f );
		break;
	default:
		if (pEdge->enabled)
		{
			color = ARROW_COLOR;
		}
		else
		{
				color = ARROW_DIS_COLOR;
				pen.SetWidth(2.0f);
				pen.SetDashStyle(Gdiplus::DashStyleDot);
		}
		break;
	}

	pen.SetColor(color);
	
	Gdiplus::AdjustableArrowCap cap(5*zoom,6*zoom);
	pen.SetCustomEndCap( &cap );

	const float HELPERSIZE = 4.0f;

	Gdiplus::RectF helperRect( center.X-HELPERSIZE/2, center.Y-HELPERSIZE/2, HELPERSIZE, HELPERSIZE );

	if (m_bSplineArrows)
	{
		if (pEdge->cornerModified)
		{
			float cdx = pEdge->cornerPoints[0].X - center.X;
			float cdy = pEdge->cornerPoints[0].Y - center.Y;

			dx += cdx;
			dy += cdy;

			pnts[0] = Gdiplus::PointF(pout.X,pout.Y);
			pnts[1] = Gdiplus::PointF(pnts[0].X+h[dout].x1*dx,pnts[0].Y+h[dout].y1*dy);
			pnts[3] = Gdiplus::PointF(pin.X,pin.Y);
			pnts[2] = Gdiplus::PointF(pnts[3].X+h[din].x2*dx,pnts[3].Y+h[din].y2*dy);
		}
		else
			pEdge->cornerPoints[0] = center;

		gr.DrawBeziers( &pen,pnts, 4 );

		if (helper && m_zoom > fHelperDrawMinimumZoom)
		{
			gr.DrawEllipse( &pen, helperRect );
		}

		if (4 == selection && pEdge && m_zoom > fHelperDrawMinimumZoom)
		{
			if (pEdge->debugCount > 1)
			{
				Gdiplus::Font font(L"Tahoma", 9.0f);
				Gdiplus::PointF point(center.X, center.Y - 15.0f);
				Gdiplus::SolidBrush brush( color );

				wchar_t buf[32];
				swprintf_s(buf, L"%d", pEdge->debugCount);
				gr.DrawString(buf, -1, &font, point, &brush);
			}
		}
	}
	else
	{
		float w = 20 + pEdge->nPortOut*10;
		if (w > fabs((pout.X-pin.X)/2))
			w = fabs((pout.X-pin.X)/2);

		if (!pEdge->cornerModified)
		{
			pEdge->cornerW = w;
			pEdge->cornerH = 40;
		}
		w = pEdge->cornerW;
		float ph = pEdge->cornerH;

		if (pin.X >= pout.X)
		{
			pnts[0] = pout;
			pnts[1] = Gdiplus::PointF(pout.X+w,pout.Y);
			pnts[2] = Gdiplus::PointF(pout.X+w,pin.Y);
			pnts[3] = pin;
			gr.DrawLines( &pen,pnts,4 );

			pEdge->cornerPoints[0] = pnts[1];
			pEdge->cornerPoints[1] = pnts[2];
			pEdge->cornerPoints[2] = Gdiplus::PointF(0,0);
			pEdge->cornerPoints[3] = Gdiplus::PointF(0,0);
		}
		else
		{
			pnts[0] = pout;
			pnts[1] = Gdiplus::PointF(pout.X+w,pout.Y);
			pnts[2] = Gdiplus::PointF(pout.X+w,pout.Y+ph);
			pnts[3] = Gdiplus::PointF(pin.X-w,pout.Y+ph);
			pnts[4] = Gdiplus::PointF(pin.X-w,pin.Y);
			pnts[5] = pin;
			gr.DrawLines( &pen,pnts,6 );

			pEdge->cornerPoints[0] = pnts[1];
			pEdge->cornerPoints[1] = pnts[2];
			pEdge->cornerPoints[2] = pnts[3];
			pEdge->cornerPoints[3] = pnts[4];
		}
		if (helper)
		{
			gr.DrawRectangle( &pen,Gdiplus::RectF(pnts[1].X-HELPERSIZE/2,pnts[1].Y-HELPERSIZE/2,HELPERSIZE,HELPERSIZE ) );
			gr.DrawRectangle( &pen,Gdiplus::RectF(pnts[2].X-HELPERSIZE/2,pnts[2].Y-HELPERSIZE/2,HELPERSIZE,HELPERSIZE ) );
		}
	}

	return helperRect;
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::CaptureMouse()
{
	if (GetCapture() != this)
	{
		SetCapture();
	}
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::ReleaseMouse()
{
	if (GetCapture() == this)
	{
		ReleaseCapture();
	}
}

//////////////////////////////////////////////////////////////////////////
CHyperNode* CHyperGraphView::GetNodeAtPoint( CPoint point )
{
	if (!m_pGraph)
		return 0;
	std::multimap<int, CHyperNode*> nodes;
	if (!GetNodesInRect( CRect(point.x,point.y,point.x,point.y),nodes ))
		return 0;

	return nodes.rbegin()->second;
}


//////////////////////////////////////////////////////////////////////////
CHyperNode* CHyperGraphView::GetMouseSensibleNodeAtPoint( CPoint point )
{
	CHyperNode* pNode = GetNodeAtPoint( point );
	if (pNode && pNode->GetObjectAt( GraphicsHelper(this), ViewToLocal(point) )==eSOID_InputTransparent)
		pNode = NULL;
		
	return pNode;
}


//////////////////////////////////////////////////////////////////////////
bool CHyperGraphView::GetNodesInRect( const CRect &viewRect,std::multimap<int, CHyperNode*> &nodes,bool bFullInside )
{
	if (!m_pGraph)
		return false;
	bool bFirst = true;
	Gdiplus::RectF localRect = ViewToLocalRect(viewRect);
	IHyperGraphEnumerator *pEnum = m_pGraph->GetNodesEnumerator();
	nodes.clear();
	
	for (IHyperNode *pINode = pEnum->GetFirst(); pINode; pINode = pEnum->GetNext())
	{
		CHyperNode *pNode = (CHyperNode*)pINode;
		const Gdiplus::RectF &itemRect = pNode->GetRect();

		if (!localRect.IntersectsWith(itemRect))
			continue;
		if (bFullInside && !localRect.Contains(itemRect))
			continue;

		nodes.insert( std::make_pair( pNode->GetDrawPriority(), pNode ));
	}
	pEnum->Release();
	
	return !nodes.empty();
}

//////////////////////////////////////////////////////////////////////////
bool CHyperGraphView::GetSelectedNodes( std::vector<CHyperNode*> &nodes, SelectionSetType setType )
{
	if (!m_pGraph)
		return false;

	bool onlyParents = false;
	bool includeRelatives = false;
	switch (setType)
	{
	case SELECTION_SET_INCLUDE_RELATIVES:
		includeRelatives = true;
		break;
	case SELECTION_SET_ONLY_PARENTS:
		onlyParents = true;
		break;
	case SELECTION_SET_NORMAL:
		break;
	}

	if (includeRelatives)
	{
		std::set<CHyperNode*> nodeSet;

		IHyperGraphEnumerator *pEnum = m_pGraph->GetNodesEnumerator();
		for (IHyperNode *pINode = pEnum->GetFirst(); pINode; pINode = pEnum->GetNext())
		{
			CHyperNode *pNode = (CHyperNode*)pINode;
			if (pNode->IsSelected())
			{
				//nodes.push_back( pNode );
				IHyperGraphEnumerator* pRelativesEnum = pNode->GetRelatedNodesEnumerator();
				for (IHyperNode *pRelative = pRelativesEnum->GetFirst(); pRelative; pRelative = pRelativesEnum->GetNext())
					nodeSet.insert(static_cast<CHyperNode*>(pRelative));
				pRelativesEnum->Release();
			}
		}
		pEnum->Release();

		nodes.clear();
		nodes.reserve(nodeSet.size());
		std::copy(nodeSet.begin(), nodeSet.end(), std::back_inserter(nodes));
	}
	else
	{
		IHyperGraphEnumerator *pEnum = m_pGraph->GetNodesEnumerator();
		nodes.clear();
		for (IHyperNode *pINode = pEnum->GetFirst(); pINode; pINode = pEnum->GetNext())
		{
			CHyperNode *pNode = (CHyperNode*)pINode;
			if (pNode->IsSelected() && (!onlyParents || pNode->GetParent() == 0))
				nodes.push_back( pNode );
		}
		pEnum->Release();
	}

	return !nodes.empty();
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::ClearSelection()
{
	if (!m_pGraph)
		return;

	IHyperGraphEnumerator *pEnum = m_pGraph->GetNodesEnumerator();
	for (IHyperNode *pINode = pEnum->GetFirst(); pINode; pINode = pEnum->GetNext())
	{
		CHyperNode *pNode = (CHyperNode*)pINode;
		if (pNode->IsSelected())
			pNode->SetSelected(false);
	}
	OnSelectionChange();
	pEnum->Release();
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::MoveSelectedNodes( CPoint offset )
{
	if(offset==CPoint(0,0))
		return;

	std::vector<CHyperNode*> nodes;
	if (!GetSelectedNodes( nodes ))
		return;

	Gdiplus::RectF bounds;

	for (int i = 0; i < nodes.size(); i++)
	{
		// Only move parent nodes.
		if (nodes[i]->GetParent() == 0)
		{
			if (m_moveHelper.find(nodes[i]) == m_moveHelper.end())
				m_moveHelper[nodes[i]] = nodes[i]->GetPos();

			if (!i)
				bounds = nodes[i]->GetRect();
			else
				Gdiplus::RectF::Union( bounds, bounds, nodes[i]->GetRect() );
			//InvalidateRect( LocalToViewRect(rect),FALSE );
			
			Gdiplus::PointF pos = nodes[i]->GetPos();
			Gdiplus::PointF firstPos = m_moveHelper[nodes[i]];
			pos.X = firstPos.X + offset.x / m_zoom;
			pos.Y = firstPos.Y + offset.y / m_zoom;
			
			// Snap rectangle to the grid.
			if (nodes[i]->IsGridBound())
			{
				pos.X = floor(((float)pos.X/GRID_SIZE) + 0.5f) * GRID_SIZE;
				pos.Y = floor(((float)pos.Y/GRID_SIZE) + 0.5f) * GRID_SIZE;
			}

			nodes[i]->SetPos( pos );
			InvalidateNode( nodes[i] );
			IHyperGraphEnumerator* pRelativesEnum = nodes[i]->GetRelatedNodesEnumerator();
			for (IHyperNode* pRelative = pRelativesEnum->GetFirst(); pRelative; pRelative = pRelativesEnum->GetNext())
			{
				CHyperNode* pRelativeNode = static_cast<CHyperNode*>(pRelative);
				Gdiplus::RectF::Union(m_workingArea,m_workingArea,pRelativeNode->GetRect());
				Gdiplus::RectF::Union( bounds, bounds, pRelativeNode->GetRect() );
			}
			pRelativesEnum->Release();
		}
	}
	CRect invRect = LocalToViewRect(bounds);
	invRect.InflateRect(32,32,32,32);
	InvalidateRect( invRect );
	SetScrollExtents();
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::OnSelectEntity()
{
	std::vector<CHyperNode*> nodes;
	if (!GetSelectedNodes( nodes ))
		return;

	CUndo undo( "Select Object(s)" );
	GetIEditor()->ClearSelection();
	for (int i = 0; i < nodes.size(); i++)
	{
		// only can CFlowNode* if not a comment, argh...
		if (!nodes[i]->IsEditorSpecialNode())
		{
			CFlowNode *pFlowNode = (CFlowNode*)nodes[i];
			if (pFlowNode->GetEntity())
			{
				GetIEditor()->SelectObject( pFlowNode->GetEntity() );
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::OnToggleMinimize()
{
	std::vector<CHyperNode*> nodes;
	if (!GetSelectedNodes( nodes ))
		return;

	for (int i = 0; i < nodes.size(); i++)
	{
		OnToggleMinimizeNode(nodes[i]);
	}
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::OnToggleMinimizeNode( CHyperNode *pNode )
{
	bool bMinimize = false;
	if (!bMinimize)
	{
		for (int i = 0; i < pNode->GetInputs()->size(); i++)
		{
			CHyperNodePort *pPort = &pNode->GetInputs()->at(i);
			if (pPort->bVisible && (pPort->nConnected==0))
			{
				bMinimize = true;
				break;
			}
		}
	}
	if (!bMinimize)
	{
		for (int i = 0; i < pNode->GetOutputs()->size(); i++)
		{
			CHyperNodePort *pPort = &pNode->GetOutputs()->at(i);
			if (pPort->bVisible && (pPort->nConnected==0))
			{
				bMinimize = true;
				break;
			}
		}
	}

	bool bVisible = !bMinimize;
	{
		for (int i = 0; i < pNode->GetInputs()->size(); i++)
		{
			CHyperNodePort *pPort = &pNode->GetInputs()->at(i);
			pPort->bVisible = bVisible;
		}
	}
	{
		for (int i = 0; i < pNode->GetOutputs()->size(); i++)
		{
			CHyperNodePort *pPort = &pNode->GetOutputs()->at(i);
			pPort->bVisible = bVisible;
		}
	}
	InvalidateNode(pNode,true);
	Invalidate();
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::ShowContextMenu( CPoint point,CHyperNode *pNode )
{
	if (!m_pGraph)
		return;

	SW_TEST_GRAPH_MOD(m_pGraph, true)

	CMenu menu;
	menu.CreatePopupMenu();

	std::vector<CString> classes;
	SClassMenuGroup classMenu;
	CMenu inputsMenu, outputsMenu, commentsMenu, selectionMenu;

	CPoint screenPoint = point;
	ClientToScreen(&screenPoint);

	// only add cut, delete... entries to the context menu if it is not the port context menu
	bool isPort = false;

	if (pNode && pNode->IsFlowNode())
	{
		CHyperNodePort* pPort = pNode->GetPortAtPoint(GraphicsHelper(this), ViewToLocal(point));

		if(pPort)
		{
			isPort = true;
			CMenu menu;
			menu.CreatePopupMenu();

			if (pPort->bInput == 1 && pPort->nPortIndex == 0)
			{
				if (m_pGraph->GetAIAction() != 0)
				{
					menu.AppendMenu( MF_STRING,ID_GRAPHVIEW_TARGET_GRAPH_ENTITY,"Assign User entity" );
					menu.AppendMenu( MF_STRING,ID_GRAPHVIEW_TARGET_GRAPH_ENTITY2,"Assign Object entity" );
					menu.AppendMenu( MF_SEPARATOR );
				}
				else if (pNode->CheckFlag(EHYPER_NODE_ENTITY))
				{
					menu.AppendMenu( MF_STRING,ID_GRAPHVIEW_TARGET_SELECTED_ENTITY,"Assign selected entity" );
					menu.AppendMenu( MF_STRING,ID_GRAPHVIEW_TARGET_GRAPH_ENTITY,"Assign graph entity" );
					menu.AppendMenu( MF_SEPARATOR );
				}
			}

			CFlowNode* pFlowNode = static_cast<CFlowNode*>(pNode);
			CFlowGraphDebuggerEditor* pFlowgraphDebuggerEditor = GetIEditor()->GetFlowGraphDebuggerEditor();
			assert(pFlowgraphDebuggerEditor);

			const bool portHasBreakPoint = pFlowgraphDebuggerEditor->HasBreakpoint(pFlowNode, pPort);
			const bool nodeHasBreakPoints = pFlowgraphDebuggerEditor->HasBreakpoint(pFlowNode->GetIFlowGraph(), pFlowNode->GetFlowNodeId());
			const bool graphHasBreakPoints = pFlowgraphDebuggerEditor->HasBreakpoint(pFlowNode->GetIFlowGraph());

			menu.AppendMenu(MF_STRING|(portHasBreakPoint || (pPort->nConnected==0 && pPort->bInput))?MF_GRAYED:0, ID_GRAPHVIEW_ADD_BREAKPOINT, _T("Add Breakpoint"));
			menu.AppendMenu(MF_STRING|(false==portHasBreakPoint)?MF_GRAYED:0, ID_GRAPHVIEW_REMOVE_BREAKPOINT, _T("Remove Breakpoint"));
			menu.AppendMenu( MF_SEPARATOR );
			menu.AppendMenu(MF_STRING|(false==nodeHasBreakPoints)?MF_GRAYED:0, ID_GRAPHVIEW_REMOVE_BREAKPOINTS_FOR_NODE, _T("Remove Breakpoints For Node"));
			menu.AppendMenu(MF_STRING|(false==graphHasBreakPoints)?MF_GRAYED:0, ID_GRAPHVIEW_REMOVE_ALL_BREAKPOINTS, _T("Remove Breakpoints For Graph"));

			menu.AppendMenu( MF_SEPARATOR );

			SFlowAddress addr;
			addr.node = pFlowNode->GetFlowNodeId();
			addr.port = pPort->nPortIndex;
			addr.isOutput = !pPort->bInput;

			IFlowGraphDebuggerConstPtr pFlowgraphDebugger = GetIFlowGraphDebuggerPtr();
			const bool bIsEnabled = pFlowgraphDebugger ? pFlowgraphDebugger->IsBreakpointEnabled(pFlowNode->GetIFlowGraph(), addr) : false;
			const bool bIsTracepoint = pFlowgraphDebugger ? pFlowgraphDebugger->IsTracepoint(pFlowNode->GetIFlowGraph(), addr) : false;

			UINT enableFlags = (bIsEnabled && portHasBreakPoint) ? MF_CHECKED : !portHasBreakPoint ? MF_GRAYED : MF_UNCHECKED;
			UINT tracepointFlags = (bIsTracepoint && portHasBreakPoint) ? MF_CHECKED : !portHasBreakPoint ? MF_GRAYED : MF_UNCHECKED;
			
			menu.AppendMenu(MF_STRING | enableFlags, ID_BREAKPOINT_ENABLE, "Enabled");
			menu.AppendMenu(MF_STRING | tracepointFlags, ID_TRACEPOINT_ENABLE, "Tracepoint");

			CPoint pt;
			GetCursorPos(&pt);

			switch (menu.TrackPopupMenuEx(TPM_RETURNCMD, pt.x, pt.y, this, NULL))
			{
			case ID_GRAPHVIEW_ADD_BREAKPOINT:
				{
					if (false == portHasBreakPoint && (pPort->nConnected || !pPort->bInput))
					{
						pFlowgraphDebuggerEditor->AddBreakpoint(pFlowNode, pPort);
					}
				}
				break;
			case ID_GRAPHVIEW_REMOVE_BREAKPOINT:
				{
					if (portHasBreakPoint)
					{
						pFlowgraphDebuggerEditor->RemoveBreakpoint(pFlowNode, pPort);
					}
				}
				break;
			case ID_GRAPHVIEW_REMOVE_BREAKPOINTS_FOR_NODE:
					{
							pFlowgraphDebuggerEditor->RemoveAllBreakpointsForNode(pFlowNode);
					}
					break;
			case ID_GRAPHVIEW_REMOVE_ALL_BREAKPOINTS:
				{
						pFlowgraphDebuggerEditor->RemoveAllBreakpointsForGraph(pFlowNode->GetIFlowGraph());
				}
				break;
			case ID_BREAKPOINT_ENABLE:
					{
							pFlowgraphDebuggerEditor->EnableBreakpoint(pFlowNode, pPort, !bIsEnabled);
					}
					break;
			case ID_TRACEPOINT_ENABLE:
					{
							pFlowgraphDebuggerEditor->EnableTracepoint(pFlowNode, pPort, !bIsTracepoint);
					}
					break;
			case ID_GRAPHVIEW_TARGET_SELECTED_ENTITY:
				{
					CFlowNode *pFlowNode = (CFlowNode*)pNode;
					GetIEditor()->GetFlowGraphManager()->SendNotifyEvent(EHG_NODE_UPDATE_ENTITY, 0, pFlowNode);
					pFlowNode->SetSelectedEntity();
					pFlowNode->Invalidate(true);
					GetIEditor()->GetFlowGraphManager()->SendNotifyEvent(EHG_NODE_UPDATE_ENTITY, 0, pFlowNode);
				}
				break;
			case ID_GRAPHVIEW_TARGET_GRAPH_ENTITY:
				{
					CFlowNode *pFlowNode = (CFlowNode*)pNode;
					GetIEditor()->GetFlowGraphManager()->SendNotifyEvent(EHG_NODE_UPDATE_ENTITY, 0, pFlowNode);
					pFlowNode->SetDefaultEntity();
					pFlowNode->Invalidate(true);
					GetIEditor()->GetFlowGraphManager()->SendNotifyEvent(EHG_NODE_UPDATE_ENTITY, 0, pFlowNode);
				}
				break;
			case ID_GRAPHVIEW_TARGET_GRAPH_ENTITY2:
				{
					CFlowNode *pFlowNode = (CFlowNode*)pNode;
					pFlowNode->SetFlag( EHYPER_NODE_GRAPH_ENTITY,false );
					pFlowNode->SetFlag( EHYPER_NODE_GRAPH_ENTITY2,true );
				}
				break;
			}
		}
		else
		{
			menu.AppendMenu( MF_STRING,ID_GRAPHVIEW_RENAME,"Rename" );
			menu.AppendMenu( MF_SEPARATOR );

			inputsMenu.CreatePopupMenu();
			outputsMenu.CreatePopupMenu();

			menu.AppendMenu( MF_POPUP,(UINT_PTR)inputsMenu.GetSafeHmenu(),"Inputs" );
			menu.AppendMenu( MF_POPUP,(UINT_PTR)outputsMenu.GetSafeHmenu(),"Outputs" );

			//////////////////////////////////////////////////////////////////////////
			// Inputs.
			//////////////////////////////////////////////////////////////////////////
			for (int i = 0; i < pNode->GetInputs()->size(); i++)
			{
				CHyperNodePort *pPort = &pNode->GetInputs()->at(i);
				inputsMenu.AppendMenu( MF_STRING |
					((pPort->bVisible||pPort->nConnected!=0)?MF_CHECKED:0) |
					((pPort->nConnected!=0)?MF_GRAYED:0),
					BASE_INPUTS_CMD+i,pPort->GetHumanName() );
			}
			{
				int numEnabled = 0;
				int numDisabled = 0;
				std::vector<CHyperEdge*> edges;
				if (m_pGraph->FindEdges(pNode, edges))
				{
					for (int i = 0; i < edges.size(); ++i)
					{
						if(pNode == m_pGraph->FindNode(edges[i]->nodeIn))
						{
							if(edges[i]->enabled)
								numEnabled++;
							else
								numDisabled++;
						}
					}
				}

				bool isPaste = false;
				CClipboard clipboard;
				XmlNodeRef node = clipboard.Get();
				if( node && node->isTag("GraphNodeInputLinks"))
					isPaste = true;

				inputsMenu.AppendMenu( MF_SEPARATOR );
				inputsMenu.AppendMenu( MF_STRING | (numEnabled ? 0 : MF_GRAYED),ID_INPUTS_DISABLE_LINKS,"Disable Links" );
				inputsMenu.AppendMenu( MF_STRING | (numDisabled ? 0 : MF_GRAYED),ID_INPUTS_ENABLE_LINKS,"Enable Links" );
				inputsMenu.AppendMenu( MF_SEPARATOR );
				inputsMenu.AppendMenu( MF_STRING | ((numEnabled || numDisabled) ? 0 : MF_GRAYED),ID_INPUTS_CUT_LINKS,"Cut Links" );
				inputsMenu.AppendMenu( MF_STRING | ((numEnabled || numDisabled) ? 0 : MF_GRAYED),ID_INPUTS_COPY_LINKS,"Copy Links" );
				inputsMenu.AppendMenu( MF_STRING | (isPaste ? 0 : MF_GRAYED),ID_INPUTS_PASTE_LINKS,"Paste Links" );
				inputsMenu.AppendMenu( MF_STRING | ((numEnabled || numDisabled) ? 0 : MF_GRAYED),ID_INPUTS_DELETE_LINKS,"Delete Links" );
			}
			inputsMenu.AppendMenu( MF_SEPARATOR );
			inputsMenu.AppendMenu( MF_STRING,ID_INPUTS_SHOW_ALL,"Show All" );
			inputsMenu.AppendMenu( MF_STRING,ID_INPUTS_HIDE_ALL,"Hide All" );
			//////////////////////////////////////////////////////////////////////////

			//////////////////////////////////////////////////////////////////////////
			// Outputs.
			//////////////////////////////////////////////////////////////////////////
			for (int i = 0; i < pNode->GetOutputs()->size(); i++)
			{
				CHyperNodePort *pPort = &pNode->GetOutputs()->at(i);
				outputsMenu.AppendMenu( MF_STRING | 
					((pPort->bVisible||pPort->nConnected!=0)?MF_CHECKED:0) |
					((pPort->nConnected!=0)?MF_GRAYED:0),
					BASE_OUTPUTS_CMD+i,pPort->GetHumanName() );
			}

			{
				int numEnabled = 0;
				int numDisabled = 0;
				std::vector<CHyperEdge*> edges;
				if (m_pGraph->FindEdges(pNode, edges))
				{
					for (int i = 0; i < edges.size(); ++i)
					{
						if(pNode == m_pGraph->FindNode(edges[i]->nodeOut))
						{
							if(edges[i]->enabled)
								numEnabled++;
							else
								numDisabled++;
						}
					}
				}

				bool isPaste = false;
				CClipboard clipboard;
				XmlNodeRef node = clipboard.Get();
				if( node && node->isTag("GraphNodeOutputLinks"))
					isPaste = true;

				outputsMenu.AppendMenu( MF_SEPARATOR );
				outputsMenu.AppendMenu( MF_STRING | (numEnabled ? 0 : MF_GRAYED),ID_OUTPUTS_DISABLE_LINKS,"Disable Links" );
				outputsMenu.AppendMenu( MF_STRING | (numDisabled ? 0 : MF_GRAYED),ID_OUTPUTS_ENABLE_LINKS,"Enable Links" );
				outputsMenu.AppendMenu( MF_SEPARATOR );
				outputsMenu.AppendMenu( MF_STRING | ((numEnabled || numDisabled) ? 0 : MF_GRAYED),ID_OUTPUTS_CUT_LINKS,"Cut Links" );
				outputsMenu.AppendMenu( MF_STRING | ((numEnabled || numDisabled) ? 0 : MF_GRAYED),ID_OUTPUTS_COPY_LINKS,"Copy Links" );
				outputsMenu.AppendMenu( MF_STRING | (isPaste ? 0 : MF_GRAYED),ID_OUTPUTS_PASTE_LINKS,"Paste Links" );
				outputsMenu.AppendMenu( MF_STRING | ((numEnabled || numDisabled) ? 0 : MF_GRAYED),ID_OUTPUTS_DELETE_LINKS,"Delete Links" );
			}

			outputsMenu.AppendMenu( MF_SEPARATOR );
			outputsMenu.AppendMenu( MF_STRING,ID_OUTPUTS_SHOW_ALL,"Show All" );
			outputsMenu.AppendMenu( MF_STRING,ID_OUTPUTS_HIDE_ALL,"Hide All" );
			//////////////////////////////////////////////////////////////////////////

			menu.AppendMenu( MF_SEPARATOR );
		}
	}
	else
	{
		PopulateClassMenu( classMenu, classes );
		menu.AppendMenu( MF_POPUP,(UINT_PTR)classMenu.pMenu->GetSafeHmenu(),"Add Node" );
		menu.AppendMenu( MF_STRING,ID_GRAPHVIEW_ADD_SELECTED_ENTITY,"Add Selected Entity" );
		if (m_pGraph->GetAIAction() == 0)
		{
			menu.AppendMenu( MF_STRING,ID_GRAPHVIEW_ADD_DEFAULT_ENTITY,"Add Graph Default Entity" );
		}
		commentsMenu.CreatePopupMenu();
		menu.AppendMenu( MF_POPUP, (UINT_PTR)commentsMenu.GetSafeHmenu(), "Add Comment" );

		commentsMenu.AppendMenu( MF_STRING, ID_GRAPHVIEW_ADD_COMMENT, "Add Simple Comment" );
		commentsMenu.AppendMenu( MF_STRING, ID_GRAPHVIEW_ADD_COMMENTBOX, "Add Comment Box" );
		commentsMenu.AppendMenu( MF_STRING, ID_GRAPHVIEW_ADD_BLACK_BOX, "Add BlackBox" );

		menu.AppendMenu( MF_STRING,ID_GRAPHVIEW_ADD_TRACKEVENT,"Add Track Event Node" );
		menu.AppendMenu( MF_STRING,ID_GRAPHVIEW_ADD_STARTNODE,"Add Start Node" );
		menu.AppendMenu( MF_SEPARATOR );
	}

	// not doing this right now -> ensuring consistent (=broken!) UI
	// CClipboard clipboard;
	// int pasteFlags = clipboard.IsEmpty() ? MF_GRAYED : 0;

	if(false == isPort)
	{
		menu.AppendMenu( MF_STRING,ID_EDIT_CUT,"Cut" );
		menu.AppendMenu( MF_STRING,ID_EDIT_COPY,"Copy" );
		menu.AppendMenu( MF_STRING,ID_EDIT_PASTE,"Paste" );
		menu.AppendMenu( MF_STRING,ID_EDIT_PASTE_WITH_LINKS,"Paste with Links" );
		menu.AppendMenu( MF_STRING,ID_EDIT_DELETE,"Delete" );
		menu.AppendMenu( MF_SEPARATOR );
		//menu.AppendMenu( MF_STRING,ID_FILE_EXPORTSELECTION,"Export Selected" );
		selectionMenu.CreatePopupMenu();
		menu.AppendMenu( MF_POPUP, (UINT_PTR)selectionMenu.GetSafeHmenu(), "Selection" );

		selectionMenu.AppendMenu( MF_STRING, ID_FILE_EXPORTSELECTION, "Export Selected Nodes" );
		selectionMenu.AppendMenu( MF_STRING, ID_GRAPHVIEW_SELECT_ENTITY, "Select Assigned Entity" );
		selectionMenu.AppendMenu( MF_STRING, ID_GRAPHVIEW_SELECT_AND_GOTO_ENTITY, "Select and Goto Assigned Entity" );

		menu.AppendMenu( MF_STRING,ID_FILE_IMPORT,"Import" );
		//menu.AppendMenu( MF_STRING,ID_GRAPHVIEW_SELECT_ENTITY,"Select Assigned Entity" );

		menu.AppendMenu( MF_STRING|(m_bSplineArrows?(MF_CHECKED):0),ID_GRAPHVIEW_SPLINES,"Show Spline Arrows" );
		menu.AppendMenu( MF_STRING,ID_GRAPHVIEW_FIT_TOVIEW,"Fit Graph to View" );
	}
	
	int cmd = menu.TrackPopupMenu( TPM_RETURNCMD|TPM_LEFTALIGN|TPM_LEFTBUTTON|TPM_NONOTIFY,screenPoint.x,screenPoint.y,this );

	if (cmd >= BASE_NEW_NODE_CMD && cmd < BASE_NEW_NODE_CMD+classes.size())
	{
		CString cls = classes[cmd-BASE_NEW_NODE_CMD];
		CreateNode( cls,point );
		return;
	}

	if (cmd >= BASE_INPUTS_CMD && cmd < BASE_INPUTS_CMD+1000)
	{
		if (pNode)
		{
			CUndo undo( "Graph Port Visibilty" );
			pNode->RecordUndo();
			int nPort = cmd - BASE_INPUTS_CMD;
			pNode->GetInputs()->at(nPort).bVisible = !pNode->GetInputs()->at(nPort).bVisible;
			InvalidateNode(pNode,true);
			Invalidate();
		}
		return;
	}
	if (cmd >= BASE_OUTPUTS_CMD && cmd < BASE_OUTPUTS_CMD+1000)
	{
		if (pNode)
		{
			CUndo undo( "Graph Port Visibilty" );
			pNode->RecordUndo();
			int nPort = cmd - BASE_OUTPUTS_CMD;
			pNode->GetOutputs()->at(nPort).bVisible = !pNode->GetOutputs()->at(nPort).bVisible;
			InvalidateNode(pNode,true);
			Invalidate();
		}
		return;
	}


	switch (cmd)
	{
	case ID_EDIT_CUT:
		OnCommandCut();
		break;
	case ID_EDIT_COPY:
		OnCommandCopy();
		break;
	case ID_EDIT_PASTE:
		InternalPaste(false, point);
		break;
	case ID_EDIT_PASTE_WITH_LINKS:
		InternalPaste(true, point);
		break;
	case ID_EDIT_DELETE:
		OnCommandDelete();
		break;
	case ID_GRAPHVIEW_RENAME:
		RenameNode( pNode );
		break;
	case ID_FILE_EXPORTSELECTION:
	case ID_FILE_IMPORT:
		if (GetParent())
			GetParent()->SendMessage( WM_COMMAND,MAKEWPARAM(cmd,0),NULL );
		break;
	case ID_GRAPHVIEW_ADD_SELECTED_ENTITY:
		{
			CreateNode( "selected_entity",point );
		}
		break;
	case ID_GRAPHVIEW_SPLINES:
		m_bSplineArrows = !m_bSplineArrows;
		Invalidate();
		break;
	case ID_GRAPHVIEW_ADD_DEFAULT_ENTITY:
		{
			CreateNode( "default_entity",point );
		}
		break;
	case ID_GRAPHVIEW_ADD_COMMENT:
		{
			CHyperNode *pNode = CreateNode( CCommentNode::GetClassType(), point );
			if (pNode)
				RenameNode( pNode );
		}
		break;
	case ID_GRAPHVIEW_ADD_COMMENTBOX:
		{
			std::vector<CHyperNode*> nodes;
			GetSelectedNodes(nodes, SELECTION_SET_ONLY_PARENTS);

			CCommentBoxNode *pNewNode = static_cast<CCommentBoxNode*>(CreateNode( CCommentBoxNode::GetClassType(), point ));
			
			// if there are selected nodes, and they are on view, the created commentBox encloses all of them			
			if (nodes.size()>1)
			{
				Gdiplus::RectF totalRect = nodes[0]->GetRect();
				for(int i = 1; i < nodes.size(); ++i)
				{
					CHyperNode* pSelNode = nodes[i];
					totalRect.Union( totalRect, totalRect, pSelNode->GetRect() );
				}
				totalRect.Width = ceilf( totalRect.Width );
				totalRect.Height = ceilf( totalRect.Height );

				CRect screenRc;
				GetClientRect( screenRc );
				CRect selectRc = LocalToViewRect( totalRect );
				CRect intersectionRc;
				bool selectedNodesAreOnView = intersectionRc.IntersectRect( screenRc, selectRc );

				if (selectedNodesAreOnView)
				{
					const float MARGIN = 10;
					totalRect.Inflate( MARGIN, MARGIN );
					pNewNode->SetBorderRect( totalRect );
				}
			}

			if (pNewNode)
				RenameNode( pNewNode );
		}
		break;
	case ID_GRAPHVIEW_ADD_BLACK_BOX:
		{
			std::vector<CHyperNode*> nodes;
			GetSelectedNodes(nodes, SELECTION_SET_ONLY_PARENTS);
			if (nodes.size() > 0)
			{
				for(int i = 0; i < nodes.size(); ++i)
				{
					if(strcmp(nodes[i]->GetClassName(), CBlackBoxNode::GetClassType()) == 0)
						return;
				}
				CHyperNode *pNode = CreateNode( CBlackBoxNode::GetClassType(), point );
				if (pNode)
				{
					RenameNode( pNode );
					CBlackBoxNode *pBB = (CBlackBoxNode*)pNode;
					for(int n = 0; n < nodes.size(); ++n)
						pBB->AddNode((CFlowNode*)(nodes[n]));
				}
			}
		}
		break;
	case ID_GRAPHVIEW_ADD_TRACKEVENT:
		{
			// Make CTrackEventNode
			CreateNode( CTrackEventNode::GetClassType(), point );
		}
		break;
	case ID_GRAPHVIEW_ADD_STARTNODE:
		{
			CreateNode( "Start", point );
		}
		break;
	case ID_GRAPHVIEW_FIT_TOVIEW:
		OnCommandFitAll();
		break;
	case ID_GRAPHVIEW_SELECT_ENTITY:
		OnSelectEntity();
		break;
	case ID_GRAPHVIEW_SELECT_AND_GOTO_ENTITY:
		{
			OnSelectEntity();
			CViewport *vp = GetIEditor()->GetActiveView();
			if (vp)
				vp->CenterOnSelection();
		}
		break;
	case ID_OUTPUTS_SHOW_ALL:
	case ID_OUTPUTS_HIDE_ALL:
		if (pNode)
		{
			CUndo undo( "Graph Port Visibilty" );
			pNode->RecordUndo();
			for (int i = 0; i < pNode->GetOutputs()->size(); i++)
			{
				pNode->GetOutputs()->at(i).bVisible = (cmd == ID_OUTPUTS_SHOW_ALL) || (pNode->GetOutputs()->at(i).nConnected != 0);
			}
			InvalidateNode(pNode,true);
			Invalidate();
		}
		break;
	case ID_INPUTS_SHOW_ALL:
	case ID_INPUTS_HIDE_ALL:
		if (pNode)
		{
			CUndo undo( "Graph Port Visibilty" );
			pNode->RecordUndo();
			for (int i = 0; i < pNode->GetInputs()->size(); i++)
			{
				pNode->GetInputs()->at(i).bVisible = (cmd == ID_INPUTS_SHOW_ALL) || (pNode->GetInputs()->at(i).nConnected != 0);
			}
			InvalidateNode(pNode,true);
			Invalidate();
		}
		break;
	case ID_OUTPUTS_DISABLE_LINKS:
	case ID_OUTPUTS_ENABLE_LINKS:
		if (pNode)
		{
			CUndo undo( "Enabling Graph Node Links" );
			pNode->RecordUndo();

			std::vector<CHyperEdge*> edges;
			if (m_pGraph->FindEdges(pNode, edges))
			{
				for (int i = 0; i < edges.size(); ++i)
				{
					if(pNode == m_pGraph->FindNode(edges[i]->nodeOut))
					{
						m_pGraph->EnableEdge(edges[i], cmd == ID_OUTPUTS_ENABLE_LINKS);
						InvalidateEdge(edges[i]);
					}
				}
			}
		}
		break;
	case ID_INPUTS_DISABLE_LINKS:
	case ID_INPUTS_ENABLE_LINKS:
		if (pNode)
		{
			CUndo undo( "Enabling Graph Node Links" );
			pNode->RecordUndo();

			std::vector<CHyperEdge*> edges;
			if (m_pGraph->FindEdges(pNode, edges))
			{
				for (int i = 0; i < edges.size(); ++i)
				{
					if(pNode == m_pGraph->FindNode(edges[i]->nodeIn))
					{
						m_pGraph->EnableEdge(edges[i], cmd == ID_INPUTS_ENABLE_LINKS);
						InvalidateEdge(edges[i]);
					}
				}
			}
		}
		break;
	case ID_INPUTS_CUT_LINKS:
	case ID_OUTPUTS_CUT_LINKS:
		{
			CUndo undo( "Cut Graph Node Links" );
			CopyLinks(pNode, cmd==ID_INPUTS_CUT_LINKS);
			DeleteLinks(pNode, cmd==ID_INPUTS_CUT_LINKS);
		}
		break;
	case ID_INPUTS_COPY_LINKS:
	case ID_OUTPUTS_COPY_LINKS:
		CopyLinks(pNode, cmd==ID_INPUTS_COPY_LINKS);
		break;
	case ID_INPUTS_PASTE_LINKS:
	case ID_OUTPUTS_PASTE_LINKS:
		{
			CUndo undo( "Paste Graph Node Links" );
      m_pGraph->RecordUndo();
			PasteLinks(pNode, cmd==ID_INPUTS_PASTE_LINKS);
		}
		break;
	case ID_INPUTS_DELETE_LINKS:
	case ID_OUTPUTS_DELETE_LINKS:
		{
			CUndo undo( "Delete Graph Node Links" );
			DeleteLinks(pNode, cmd==ID_INPUTS_DELETE_LINKS);
		}
		break;
	}
}

struct NodeFilter
{
public:
	NodeFilter(uint32 mask) : mask(mask) {}
	bool Visit (CHyperNode* pNode)
	{
		if (pNode->IsEditorSpecialNode())
			return false;
		CFlowNode *pFlowNode = static_cast<CFlowNode*> (pNode);
		if ((pFlowNode->GetCategory() & mask) == 0)
			return false;
		return true;

		// Only if the usage mask is set check if fulfilled -> this is an exclusive thing
		if ((mask&EFLN_USAGE_MASK) != 0 && (pFlowNode->GetUsageFlags() & mask) == 0)
			return false;
		return true;
	}
	uint32 mask;
};

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::PopulateClassMenu( SClassMenuGroup &classMenu, std::vector<CString> &classes )
{
	NodeFilter filter(m_componentViewMask);
	std::vector<_smart_ptr<CHyperNode> > prototypes;
	m_pGraph->GetManager()->GetPrototypesEx( prototypes, true, functor_ret(filter, &NodeFilter::Visit) );
	classes.resize(0);
	classes.reserve(prototypes.size());
	for (std::vector<_smart_ptr<CHyperNode> >::iterator iter = prototypes.begin();
		iter != prototypes.end(); ++iter)
	{
		if ((*iter)->IsFlowNode())
		{
			CString fullname = (*iter)->GetClassName();
			if (fullname.Find(':') >= 0)
			{
				classes.push_back(fullname);
			}
			else
			{
				CString name = "Misc:";
				name += fullname;
				name += "0x2410";
				classes.push_back(name);
			}
		}
	}
	std::sort( classes.begin(),classes.end() );

	PopulateSubClassMenu(classMenu, classes, BASE_NEW_NODE_CMD);
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::PopulateSubClassMenu( SClassMenuGroup &classMenu, std::vector<CString> &classes, int baseIndex )
{
	std::map<CString,SClassMenuGroup*> groupMap;
	std::list<CString> subGroups;
	for (int i = 0; i < classes.size(); i++)
	{
		const CString& fullname = classes[i];
		CString group,node;
		if (fullname.Find(':') >= 0)
		{
			group = fullname.SpanExcluding(":");
			node = fullname.Mid(group.GetLength()+1);
			int marker = node.Find("0x2410");
			if (marker>0)
			{
				node = node.Left(marker);
				classes[i] = node;
			}
		}
		else
		{
			group = "_UNKNOWN_"; // should never happen
			node = fullname;
			// assert (false);
		}

		SClassMenuGroup *pGroupMenu = &classMenu;
		if (!group.IsEmpty())
		{
			pGroupMenu = stl::find_in_map( groupMap,group,(SClassMenuGroup*)0 );
			if (!pGroupMenu)
			{
				pGroupMenu = classMenu.CreateSubMenu();
				classMenu.pMenu->AppendMenu( MF_POPUP,(UINT_PTR)pGroupMenu->pMenu->GetSafeHmenu(),group );
				groupMap[group] = pGroupMenu;
			}
		}
		else
		{
			assert(false); // should never happen
			continue;
		}

		if (node.Find(':') >= 0)
		{
			CString parentGroup = node.SpanExcluding(":");
			if (!stl::find(subGroups, parentGroup))
			{
				subGroups.push_back(parentGroup);
				std::vector<CString> subclasses;
				for (std::vector<CString>::iterator iter = classes.begin(); iter != classes.end(); ++iter)
				{
					CString subclass = iter->Mid(group.GetLength()+1);
					if (subclass.Find(':') >= 0 && subclass.SpanExcluding(":") == parentGroup)
						subclasses.push_back(subclass);
				}
				if (subclasses.size() > 0)
				{
					PopulateSubClassMenu(*pGroupMenu, subclasses, i + baseIndex);
				}
			}

		}
		else
		{
			UINT itemCount = pGroupMenu->pMenu->GetMenuItemCount();
			pGroupMenu->pMenu->AppendMenu( MF_STRING  | ( itemCount == 0 || (itemCount % 35) ? 0 : MF_MENUBREAK) ,baseIndex+i,node );
		}
	}
}
//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::ShowPortsConfigMenu( CPoint point,bool bInput,CHyperNode *pNode )
{
	CMenu menu;
	menu.CreatePopupMenu();

	CHyperNode::Ports *pPorts;
	if (bInput)
	{
		pPorts = pNode->GetInputs();
		menu.AppendMenu( MF_STRING|MF_GRAYED,0,"Inputs" );
	}
	else
	{
		pPorts = pNode->GetOutputs();
		menu.AppendMenu( MF_STRING|MF_GRAYED,0,"Outputs" );
	}
	menu.AppendMenu( MF_SEPARATOR );

	for (int i = 0; i < pPorts->size(); i++)
	{
		CHyperNodePort *pPort = &(*pPorts)[i];
		int flags = MF_STRING;
		if (pPort->bVisible || pPort->nConnected != 0)
			flags |= MF_CHECKED;
		if (pPort->nConnected != 0)
			flags |= MF_GRAYED;
		menu.AppendMenu( flags,i+1,pPort->pVar->GetName() );
	}
	menu.AppendMenu( MF_SEPARATOR );
	if (bInput)
	{
		menu.AppendMenu( MF_STRING,ID_INPUTS_SHOW_ALL,"Show All" );
		menu.AppendMenu( MF_STRING,ID_INPUTS_HIDE_ALL,"Hide All" );
	}
	else
	{
		menu.AppendMenu( MF_STRING,ID_OUTPUTS_SHOW_ALL,"Show All" );
		menu.AppendMenu( MF_STRING,ID_OUTPUTS_HIDE_ALL,"Hide All" );
	}

	ClientToScreen(&point);

	int cmd = menu.TrackPopupMenu( TPM_RETURNCMD|TPM_LEFTALIGN|TPM_LEFTBUTTON|TPM_NONOTIFY,point.x,point.y,this );
	switch (cmd)
	{
	case ID_OUTPUTS_SHOW_ALL:
	case ID_OUTPUTS_HIDE_ALL:
		if (pNode)
		{
			CUndo undo( "Graph Port Visibilty" );
			pNode->RecordUndo();
			for (int i = 0; i < pNode->GetOutputs()->size(); i++)
			{
				pNode->GetOutputs()->at(i).bVisible = (cmd == ID_OUTPUTS_SHOW_ALL) || (pNode->GetOutputs()->at(i).nConnected != 0);
			}
			InvalidateNode(pNode,true);
			Invalidate();
			return;
		}
		break;
	case ID_INPUTS_SHOW_ALL:
	case ID_INPUTS_HIDE_ALL:
		if (pNode)
		{
			CUndo undo( "Graph Port Visibilty" );
			pNode->RecordUndo();
			for (int i = 0; i < pNode->GetInputs()->size(); i++)
			{
				pNode->GetInputs()->at(i).bVisible = (cmd == ID_INPUTS_SHOW_ALL) || (pNode->GetInputs()->at(i).nConnected != 0);
			}
			InvalidateNode(pNode,true);
			Invalidate();
			return;
		}
	}
	if (cmd > 0)
	{
		CUndo undo( "Graph Port Visibilty" );
		pNode->RecordUndo();
		CHyperNodePort *pPort = &(*pPorts)[cmd-1];
		pPort->bVisible = !pPort->bVisible || (pPort->nConnected != 0);
	}
	InvalidateNode(pNode,true);
	Invalidate();
}


//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::UpdateWorkingArea()
{
	m_workingArea = Gdiplus::RectF(0,0,0,0);
	if(!m_pGraph)
	{
		m_scrollOffset.SetPoint(0,0);
		return;
	}

	bool bFirst = true;
	IHyperGraphEnumerator* pEnum = m_pGraph->GetNodesEnumerator();
	for (IHyperNode* pINode = pEnum->GetFirst(); pINode; pINode = pEnum->GetNext())
	{
		const Gdiplus::RectF &itemRect = ((CHyperNode*)pINode)->GetRect();
		if (bFirst)
		{
			m_workingArea = itemRect;
			bFirst = false;
		}
		else
		{
			Gdiplus::RectF rc = m_workingArea;
			m_workingArea.Union( m_workingArea, rc, itemRect );
		}
	}
	pEnum->Release();

	m_workingArea = Gdiplus::RectF(m_workingArea.X-GRID_SIZE, m_workingArea.Y-GRID_SIZE, m_workingArea.Width+GRID_SIZE*2, m_workingArea.Height+GRID_SIZE*2);
}


 //////////////////////////////////////////////////////////////////////////
void CHyperGraphView::SetScrollExtents(bool isUpdateWorkingArea)
{
	if(isUpdateWorkingArea)
		UpdateWorkingArea();

	static bool bNoRecurse = false;
	if (bNoRecurse)
		return;
	bNoRecurse = true;

	// Update scroll.
	CRect rcClient;
	GetClientRect(rcClient);

	Gdiplus::RectF rect = ViewToLocalRect(rcClient);
	rect.Union(rect,rect,m_workingArea);
	CRect rc = LocalToViewRect(rect);
	
	SCROLLINFO si;
	ZeroStruct(si);
	si.cbSize = sizeof(si);
	si.fMask = SIF_ALL;
	si.nMax = rc.Width();
	si.nPage = rcClient.Width() + 3;
	si.nPos = - rc.left;
	SetScrollInfo( SB_HORZ, &si, TRUE );

	si.nMax = rc.Height();
	si.nPage = rcClient.Height() + 3;
	si.nPos = - rc.top;
	SetScrollInfo( SB_VERT, &si, TRUE );

	bNoRecurse = false;

	if (m_pGraph && !rcClient.IsRectEmpty())
		m_pGraph->SetViewPosition( m_scrollOffset, m_zoom );
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
	HideEditPort();

	SCROLLINFO si;
	GetScrollInfo( SB_HORZ, &si );
	int curPos = si.nPos;

	switch (nSBCode)
	{
	case SB_LINELEFT:
		curPos -= 10;
		break;
	case SB_LINERIGHT:
		curPos += 10;
		break;
	case SB_PAGELEFT:
		curPos -= si.nPage;
		break;
	case SB_PAGERIGHT:
		curPos += si.nPage;
		break;
	case SB_THUMBPOSITION:
	case SB_THUMBTRACK:
		curPos = nPos;
		break;
	}

	m_scrollOffset.x += si.nPos - min(int(si.nMax-si.nPage), max(int(si.nMin), curPos));
	SetScrollExtents(false);
	Invalidate();

	__super::OnHScroll(nSBCode, nPos, pScrollBar);
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
	HideEditPort();

	SCROLLINFO si;
	GetScrollInfo( SB_VERT, &si );
	int curPos = si.nPos;

	switch (nSBCode)
	{
	case SB_LINEUP:
		curPos -= 10;
		break;
	case SB_LINEDOWN:
		curPos += 10;
		break;
	case SB_PAGEUP:
		curPos -= si.nPage;
		break;
	case SB_PAGEDOWN:
		curPos += si.nPage;
		break;
	case SB_THUMBPOSITION:
	case SB_THUMBTRACK:
		curPos = nPos;
		break;
	}

	m_scrollOffset.y += si.nPos - min(int(si.nMax-si.nPage), max(int(si.nMin), curPos));
	SetScrollExtents(false);
	Invalidate();

	__super::OnVScroll(nSBCode, nPos, pScrollBar);
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::RenameNode( CHyperNode *pNode )
{
	assert(pNode);
	if ( HandleRenameNode(pNode) )
		return;

	m_pRenameNode = pNode;
	CRect rc = LocalToViewRect( pNode->GetRect() );
	rc.DeflateRect(1,1);
	rc.bottom = rc.top + DEFAULT_EDIT_HEIGHT;
	if (m_renameEdit.m_hWnd)
		m_renameEdit.DestroyWindow();
	
	m_renameEdit.SetView(this);
	int nEditFlags = WS_CHILD|WS_VISIBLE|ES_AUTOHSCROLL;
	if (pNode->IsEditorSpecialNode())
	{
		nEditFlags |= ES_MULTILINE|ES_AUTOVSCROLL|ES_WANTRETURN;
		rc.bottom += 42;
	}
	m_renameEdit.Create( nEditFlags,rc,this,IDC_RENAME );
	m_renameEdit.SetWindowText( m_pRenameNode->GetName() );
	m_renameEdit.SetFont( CFont::FromHandle( (HFONT)gSettings.gui.hSystemFont) );
	m_renameEdit.SetCapture();
	m_renameEdit.SetSel(0,-1);
	m_renameEdit.SetFocus();
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::OnAcceptRename()
{
	CString text;
	m_renameEdit.GetWindowText(text);
	if (m_pRenameNode)
	{
		text.Replace( "\n","\\n" );
		text.Replace( "\r","" );
		text.Replace( " ","_");
		m_pRenameNode->SetName(text);
		InvalidateNode(m_pRenameNode);
	}
	m_pRenameNode = NULL;
	m_renameEdit.DestroyWindow();
	Invalidate();
	OnNodeRenamed();
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::OnCancelRename()
{
	m_pRenameNode = NULL;
	m_renameEdit.DestroyWindow();
	Invalidate();
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::OnSelectionChange()
{
	if (!m_pPropertiesCtrl)
		return;

	m_pEditedNode = 0;
	m_pMultiEditedNodes.resize(0);
	m_pMultiEditVars = 0;

	std::vector<CHyperNode*> nodes;
	GetSelectedNodes(nodes, SELECTION_SET_ONLY_PARENTS);
	m_pPropertiesCtrl->RemoveAllItems();

	if (nodes.size() >= 1 && !m_bIsFrozen)
	{
		CHyperNode* pNode = nodes[0];
		_smart_ptr<CVarBlock> pVarBlock = pNode->GetInputsVarBlock();
		if (pVarBlock && pNode->GetTypeId()==gEnv->pFlowSystem->GetTypeId("Engine:LayerSwitch"))
				SetupLayerList(pVarBlock);
			
		if (nodes.size() == 1)
		{
			m_pEditedNode = pNode;

			if (pVarBlock)
			{
				m_pPropertiesCtrl->AddVarBlock( pVarBlock );
				m_pPropertiesCtrl->SetDisplayOnlyModified( false );
			}
		}
		else
		{
			bool isMultiselect = false;
			// Find if we have nodes the same type like the first one
			for( int i=1; i<nodes.size(); ++i )
			{
				if( pNode->GetTypeId() == nodes[i]->GetTypeId() )
				{
					isMultiselect = true;
					break;
				}
			}

			if(isMultiselect)
			{
				if (pVarBlock)
				{
					m_pMultiEditVars = pVarBlock->Clone(true);
					m_pPropertiesCtrl->AddVarBlock( m_pMultiEditVars );
				}

				for( int i=0; i<nodes.size(); ++i )
				{
					if( pNode->GetTypeId() == nodes[i]->GetTypeId() )
					{
						m_pMultiEditedNodes.push_back(nodes[i]);
						m_pMultiEditVars->Wire( nodes[i]->GetInputsVarBlock() );
					}
				}

				m_pPropertiesCtrl->SetDisplayOnlyModified( true );
			}
		}
	}

	CWnd *pWnd = GetParent();
	if (pWnd->IsKindOf(RUNTIME_CLASS(CHyperGraphDialog)))
	{
		((CHyperGraphDialog*)pWnd)->OnViewSelectionChange();
	}
}

//////////////////////////////////////////////////////////////////////////
CHyperNode* CHyperGraphView::CreateNode( const CString &sNodeClass,CPoint point )
{
	if (!m_pGraph)
		return 0;
	
#ifdef SEG_WORLD
	if(!CSWManager::Get().GetDoc().CanModify(m_pGraph, true))
		return 0;
#endif

	Gdiplus::PointF p = ViewToLocal(point);
	CHyperNode *pNode = NULL;
	{
		CUndo undo( "New Graph Node");
		m_pGraph->UnselectAll();
		pNode = (CHyperNode*)m_pGraph->CreateNode( sNodeClass, p );
	}
	if (pNode)
	{
		pNode->SetSelected(true);
		OnSelectionChange();
	}
	InvalidateView();
	return pNode;
}


//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::UpdateNodeProperties( CHyperNode* pNode, IVariable *pVar )
{
	if(pVar->GetDataType() == IVariable::DT_UIENUM && pNode->GetTypeId()==gEnv->pFlowSystem->GetTypeId("Engine:LayerSwitch"))
	{
		CString val;
		pVar->Get(val);
		_smart_ptr<CVarBlock> pVarBlock = pNode->GetInputsVarBlock();
		IVariable* pVarInternal = pVarBlock->FindVariable("Layer");
		if(pVarInternal)
			pVarInternal->Set(val);
	}

	// TODO: ugliest way to solve this! I should find a better solution... [Dejan]
	if (pVar->GetDataType() == IVariable::DT_SOCLASS)
	{
		CString className;
		pVar->Get( className );
		CHyperNodePort* pHelperInputPort = pNode->FindPort( "sohelper_helper", true );
		if ( !pHelperInputPort )
			pHelperInputPort = pNode->FindPort( "sonavhelper_helper", true );
		if ( pHelperInputPort && pHelperInputPort->pVar )
		{
			CString helperName;
			pHelperInputPort->pVar->Get( helperName );
			int f = helperName.Find(':');
			if ( f <= 0 || className != helperName.Left(f) )
			{
				helperName = className + ':';
				pHelperInputPort->pVar->Set( helperName );
			}
		}
	}

	pNode->OnInputsChanged();
	InvalidateNode( pNode, true); // force redraw node as param values have changed
}


//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::OnUpdateProperties( IVariable *pVar )
{
	if(m_pMultiEditedNodes.size())
	{
		for (std::vector<CHyperNode*>::iterator ppNode = m_pMultiEditedNodes.begin(); ppNode != m_pMultiEditedNodes.end(); ++ppNode)
			if(*ppNode)
				UpdateNodeProperties(*ppNode, pVar);
	}
	else if (m_pEditedNode)
	{
		UpdateNodeProperties(m_pEditedNode, pVar);
	}

	if(m_pGraph && m_pGraph->IsFlowGraph())
	{
		CFlowGraph* pFlowGraph = static_cast<CFlowGraph*> (m_pGraph);
		CEntityObject* pEntity = pFlowGraph->GetEntity();
		if(pEntity)
			pEntity->SetLayerModified();
	}
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::InvalidateView(bool bComplete)
{
	std::vector<CHyperEdge*> edges;
	if ( m_pGraph )
		m_pGraph->GetAllEdges(edges);
	std::copy( edges.begin(), edges.end(), inserter(m_edgesToReroute, m_edgesToReroute.end()) );

	if (bComplete && m_pGraph)
	{
		IHyperGraphEnumerator *pEnum = m_pGraph->GetNodesEnumerator();
		for (IHyperNode *pINode = pEnum->GetFirst(); pINode; pINode = pEnum->GetNext())
		{
			CHyperNode *pNode = (CHyperNode*)pINode;
			pNode->Invalidate(true);
		}
		pEnum->Release();
	}

	Invalidate();
	SetScrollExtents();
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::FitFlowGraphToView()
{
	OnCommandFitAll();
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::ForceRedraw()
{
	if (!m_pGraph)
		return;

	InvalidateView(true);
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::OnCommandDelete()
{
	if (!m_pGraph)
		return;

	CUndo undo( "HyperGraph Delete Node(s)"  );
  m_pGraph->RecordUndo();
	std::vector<CHyperNode*> nodes;
	if (!GetSelectedNodes( nodes, SELECTION_SET_INCLUDE_RELATIVES ))
		return;

	for (int i = 0; i < nodes.size(); i++)
	{
		if(nodes[i]->CheckFlag(EHYPER_NODE_UNREMOVEABLE) == false)
			m_pGraph->RemoveNode( nodes[i] );
	}
	OnSelectionChange();
	InvalidateView();
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::OnCommandDeleteKeepLinks()
{
	if (!m_pGraph)
		return;

	CUndo undo( "HyperGraph Hide Node(s)"  );
  m_pGraph->RecordUndo();
	std::vector<CHyperNode*> nodes;
	if (!GetSelectedNodes( nodes, SELECTION_SET_INCLUDE_RELATIVES ))
		return;

	for (int i = 0; i < nodes.size(); i++)
	{
		m_pGraph->RemoveNodeKeepLinks( nodes[i] );
	}
	OnSelectionChange();
	InvalidateView();
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::OnCommandCopy()
{
	if (!m_pGraph)
		return;

	std::vector<CHyperNode*> nodes;
	if (!GetSelectedNodes( nodes ))
		return;

	m_bCopiedBlackBox = false;
	if(nodes.size() == 1 && strcmp(nodes[0]->GetClassName(), CBlackBoxNode::GetClassType()) == 0)
	{
		CBlackBoxNode *pBlackBox = (CBlackBoxNode*)nodes[0];
		nodes.clear();
		nodes = *(pBlackBox->GetNodes());
		m_bCopiedBlackBox = true; 
	}

	CClipboard clipboard;
	//clipboard.Put( )
		//clipboard.

	CHyperGraphSerializer serializer( m_pGraph, 0 );

	for (int i = 0; i < nodes.size(); i++)
	{
		CHyperNode *pNode = nodes[i];
		serializer.SaveNode( pNode, true );
	}
	if (nodes.size() > 0)
	{
		XmlNodeRef node = XmlHelpers::CreateXmlNode("Graph");
		serializer.Serialize( node,false );
		clipboard.Put( node,"Graph" );
	}
	InvalidateView();
}


//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::OnCommandPaste()
{
	if(m_bIsFrozen)
		return;
	CPoint point;
	GetCursorPos(&point);
	ScreenToClient(&point);

	CClipboard clipboard;
	XmlNodeRef node = clipboard.Get();

	if (!node)
		return;

	if(node->isTag("Graph"))
		InternalPaste(false, point);
	else
	{
		bool isInput = node->isTag("GraphNodeInputLinks");
		if( isInput || node->isTag("GraphNodeOutputLinks"))
		{
			std::vector<CHyperNode*> nodes;
			if (!GetSelectedNodes( nodes ))
				return;
			CUndo undo( "Paste Graph Node Links" );
      m_pGraph->RecordUndo();
			for (int i = 0; i < nodes.size(); i++)
			{
				PasteLinks(nodes[i], isInput);
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::OnCommandPasteWithLinks()
{
	if(m_bIsFrozen)
		return;
	CPoint point;
	GetCursorPos(&point);
	ScreenToClient(&point);
	InternalPaste(true, point);
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::InternalPaste(bool bWithLinks, CPoint point, std::vector<CHyperNode*>* pPastedNodes)
{
	if(m_bIsFrozen)
		return;

	if (!m_pGraph)
		return;

	CClipboard clipboard;
	XmlNodeRef node = clipboard.Get();

	if (node != NULL && node->isTag("Graph"))
	{
		CUndo undo( "HyperGraph Paste Node(s)"  );
		m_pGraph->UnselectAll();

		if(m_bCopiedBlackBox)
			bWithLinks = true;

		CHyperGraphSerializer serializer( m_pGraph, 0 );
		serializer.SelectLoaded(true);
		serializer.Serialize( node,true,bWithLinks,true); // Paste==true -> don't screw up graph specific data if just pasting nodes

		std::vector<CHyperNode*> nodes;
		serializer.GetLoadedNodes( nodes );
		if ( pPastedNodes )
			serializer.GetLoadedNodes( *pPastedNodes );

		if(m_bCopiedBlackBox)
		{
			CBlackBoxNode *pBB = (CBlackBoxNode*)(CreateNode( CBlackBoxNode::GetClassType(), point ));
			if (pBB)
				RenameNode( pBB );
			for(int i = 0; i < nodes.size(); ++i)
				pBB->AddNode(nodes[i]);
		}

		CRect rc;
		GetClientRect(rc);

		Gdiplus::PointF offset(100,100);
		if (point.x > 0 && point.x < rc.Width() && point.y > 0 && point.y < rc.Height())
		{
			Gdiplus::RectF bounds;
			// Calculate bounds.
			for (int i = 0; i < nodes.size(); i++)
			{
				if (i == 0)
					bounds = nodes[i]->GetRect();
				else
				{
					Gdiplus::RectF temp = bounds;
					bounds.Union(bounds,temp,nodes[i]->GetRect());
				}
			}
			Gdiplus::PointF locPoint = ViewToLocal(point);
			offset = Gdiplus::PointF(locPoint.X-bounds.X,locPoint.Y-bounds.Y);
		}
		// Offset all pasted nodes.
		for (int i = 0; i < nodes.size(); i++)
		{
			Gdiplus::RectF rc = nodes[i]->GetRect();
			rc.Offset(offset);
			nodes[i]->SetRect(rc);
		}
	}
	OnSelectionChange();
	InvalidateView();
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::OnCommandCut()
{
	OnCommandCopy();
	OnCommandDelete();
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::SimulateFlow(CHyperEdge* pEdge)
{
	// re-set target node's port value
	assert (m_pGraph != 0);
	CHyperNode *pNode = (CHyperNode*) m_pGraph->FindNode(pEdge->nodeIn);
	assert (pNode != 0);
	CFlowNode* pFlowNode = (CFlowNode*) pNode;
	IFlowGraph* pIGraph = pFlowNode->GetIFlowGraph();
	const TFlowInputData* pValue = pIGraph->GetInputValue(pFlowNode->GetFlowNodeId(), pEdge->nPortIn);
	assert (pValue != 0);
	pIGraph->ActivatePort( SFlowAddress(pFlowNode->GetFlowNodeId(),pEdge->nPortIn,false), *pValue );
}

//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::SetZoom( float zoom )
{
	m_zoom = CLAMP( zoom, MIN_ZOOM, MAX_ZOOM );
	NotifyZoomChangeToNodes();
}


//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::NotifyZoomChangeToNodes()
{	
	if (m_pGraph)
	{
		HideEditPort();
		IHyperGraphEnumerator *pEnum = m_pGraph->GetNodesEnumerator();
		for (IHyperNode *pINode = pEnum->GetFirst(); pINode; pINode = pEnum->GetNext())
		{
			CHyperNode *pNode = (CHyperNode*)pINode;
			pNode->OnZoomChange( m_zoom );
		}
		pEnum->Release();
	}
}


//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::CopyLinks(CHyperNode* pNode, bool isInput)
{
	if (!m_pGraph || !pNode)
		return;

	CHyperGraphSerializer serializer( m_pGraph, 0 );

	CClipboard clipboard;
	XmlNodeRef node = XmlHelpers::CreateXmlNode( isInput ? "GraphNodeInputLinks" : "GraphNodeOutputLinks" );

	std::vector<CHyperEdge*> edges;
	if (m_pGraph->FindEdges(pNode, edges))
	{
		for (int i = 0; i < edges.size(); ++i)
		{
			if( isInput && pNode == m_pGraph->FindNode(edges[i]->nodeIn) ||
				 !isInput && pNode == m_pGraph->FindNode(edges[i]->nodeOut))
			{
				serializer.SaveEdge( edges[i] );
			}
		}
	}
	serializer.Serialize( node, false );
	clipboard.Put( node, "Graph" );
}


//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::PasteLinks(CHyperNode* pNode, bool isInput)
{
	if (!m_pGraph || !pNode)
		return;

	CClipboard clipboard;
	XmlNodeRef node = clipboard.Get();
	if( !node || 
			isInput  && !node->isTag("GraphNodeInputLinks") || 
			!isInput && !node->isTag("GraphNodeOutputLinks"))
			return;

	XmlNodeRef edgesXml = node->findChild( "Edges" );
	if (edgesXml)
	{
		HyperNodeID nodeIn,nodeOut;
		CString portIn,portOut;
		bool bEnabled;
		bool bAskReassign = true;
		bool bIsReassign = false;
		for (int i = 0; i < edgesXml->getChildCount(); i++)
		{
			XmlNodeRef edgeXml = edgesXml->getChild(i);

			edgeXml->getAttr( "nodeIn",nodeIn );
			edgeXml->getAttr( "nodeOut",nodeOut );
			edgeXml->getAttr( "portIn",portIn );
			edgeXml->getAttr( "portOut",portOut );
			if (edgeXml->getAttr( "enabled", bEnabled) == false)
				bEnabled = true;

			if(isInput)
				nodeIn = pNode->GetId();
			else
				nodeOut = pNode->GetId();

			CHyperNode *pNodeIn = (CHyperNode*)m_pGraph->FindNode(nodeIn);
			CHyperNode *pNodeOut = (CHyperNode*)m_pGraph->FindNode(nodeOut);
			if (!pNodeIn || !pNodeOut)
				continue;
			CHyperNodePort *pPortIn = pNodeIn->FindPort(portIn,true);
			CHyperNodePort *pPortOut = pNodeOut->FindPort(portOut,false);
			if (!pPortIn || !pPortOut)
				continue;

			// check of reassigning inputs
			if(isInput)
			{
				bool bFound=false;
				std::vector<CHyperEdge*> edges;
				if (m_pGraph->FindEdges(pNodeIn, edges))
				{
					for (int i = 0; i < edges.size(); ++i)
					{
						if( pNodeIn == m_pGraph->FindNode(edges[i]->nodeIn) &&
								!stricmp(portIn, edges[i]->portIn))
						{
							bFound = true;
							break;
						}
					}
				}
				if(bFound)
				{
					if(bAskReassign)
					{
						if(MessageBox(_T("This operation tries to reassign some not empty input port(s). Do you want to reassign?"), _T("Reassign input ports"), MB_YESNO|MB_ICONQUESTION) == IDYES)
							bIsReassign = true;
						bAskReassign = false;
					}
					if(!bIsReassign)
						continue;
				}
			}

			m_pGraph->ConnectPorts( pNodeOut, pPortOut, pNodeIn, pPortIn, false );
		}
	}
}


//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::DeleteLinks(CHyperNode* pNode, bool isInput)
{
	if (!m_pGraph || !pNode)
		return;

  m_pGraph->RecordUndo();

	std::vector<CHyperEdge*> edges;
	if (m_pGraph->FindEdges(pNode, edges))
	{
		for (int i = 0; i < edges.size(); ++i)
		{
			if( isInput && pNode == m_pGraph->FindNode(edges[i]->nodeIn) ||
				 !isInput && pNode == m_pGraph->FindNode(edges[i]->nodeOut))
				m_pGraph->RemoveEdge(edges[i]);
		}
	}
}


//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::ShowEditPort(CHyperNode*pNode, CHyperNodePort* pPort)
{
	m_editParamCtrl.RemoveAllItems();
	_smart_ptr<CVarBlock> pVarBlock = pNode->GetInputsVarBlock();
	if (pVarBlock)
	{
		if(m_zoom > MIN_ZOOM_CHANGE_HEIGHT)
		{
			m_editParamCtrl.SetFontHeight(abs(gSettings.gui.nDefaultFontHieght) * m_zoom);
			m_editParamCtrl.SetItemHeight( DEFAULT_EDIT_HEIGHT * m_zoom );
		}
		else
		{
			m_editParamCtrl.SetFontHeight(0);
			m_editParamCtrl.SetItemHeight( DEFAULT_EDIT_HEIGHT );
		}

		_smart_ptr<CVarBlock> pEditVarBlock = new CVarBlock;
		pEditVarBlock->AddVariable( pPort->pVar );
		if( pNode->GetTypeId()==gEnv->pFlowSystem->GetTypeId("Engine:LayerSwitch") )
		{
			SetupLayerList(pEditVarBlock);
			m_editParamCtrl.SetUpdateCallback( functor(*this,&CHyperGraphView::OnUpdateProperties) );
			m_editParamCtrl.EnableUpdateCallback(true);
		}
		m_editParamCtrl.AddVarBlock( pEditVarBlock );

		Gdiplus::RectF rect = pNode->GetRect();
		Gdiplus::RectF rectPort;
		pNode->GetAttachRect(pPort->nPortIndex, &rectPort);
		rectPort.Y -= PORT_HEIGHT / 2;
		rectPort.Height = PORT_HEIGHT;
		rectPort.Offset( pNode->GetPos() );

		CPoint LeftTop = LocalToView( Gdiplus::PointF(rectPort.X, rectPort.Y));
		CPoint RightBottom = LocalToView( Gdiplus::PointF(rect.X + rect.Width, rectPort.Y+rectPort.Height));
		int w = RightBottom.x - LeftTop.x - 2;
		int h = MAX(m_editParamCtrl.GetVisibleHeight(), RightBottom.y - LeftTop.y);
		m_editParamCtrl.SetWindowPos( NULL, LeftTop.x+1, LeftTop.y, w, h+1, SWP_SHOWWINDOW );
		m_editParamCtrl.ShowWindow(SW_SHOW);
		CPropertyItem* pItem = m_editParamCtrl.FindItemByVar( pPort->pVar );
		if(pItem)
			m_editParamCtrl.SelectItem(pItem);

		if(rect.Width < SMALLNODE_WIDTH)
			m_editParamCtrl.SetSplitter( m_zoom > MIN_ZOOM_CHANGE_HEIGHT ? SMALLNODE_SPLITTER * m_zoom : SMALLNODE_SPLITTER);

		m_isEditPort = true;
	}
}


//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::HideEditPort()
{
	m_pEditPort = 0;
	if(!m_isEditPort)
		return;

	// Kill focus from control before Removing items to assign changes
	SetFocus();

	m_editParamCtrl.RemoveAllItems();
	m_editParamCtrl.ShowWindow(SW_HIDE);
	m_isEditPort = false;
}


//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::UpdateFrozen()
{
	bool bOldIsFrozen = m_bIsFrozen;
	m_bIsFrozen = false;
	HideEditPort();
	if(m_pGraph && m_pGraph->IsFlowGraph())
	{
		CFlowGraph* pFlowGraph = static_cast<CFlowGraph*> (m_pGraph);
		CEntityObject* pEntity = pFlowGraph->GetEntity();
		if((pEntity && pEntity->IsFrozen()) || GetIEditor()->GetGameEngine()->IsFlowSystemUpdateEnabled())
		{
			m_pGraph->UnselectAll();
			OnSelectionChange();
			m_bIsFrozen = true;
		}
		if(bOldIsFrozen != m_bIsFrozen)
			InvalidateView();
	}
}


//////////////////////////////////////////////////////////////////////////
void CHyperGraphView::SetupLayerList(CVarBlock* pVarBlock)
{
	// In the Game a Layer port has a string type, for editing we replace a port variable to the enum type variable
	IVariable* pVar = pVarBlock->FindVariable("Layer");
	if(!pVar)
		return;

	CString val;
	pVar->Get(val);
	pVarBlock->RemoveVariable(pVar);

	CVariableFlowNodeEnum<CString>* pEnumVar = new CVariableFlowNodeEnum<CString>;
	pEnumVar->SetDataType(IVariable::DT_UIENUM);
	pEnumVar->SetName("Layer");

	std::vector<CObjectLayer*> layers;
	GetIEditor()->GetObjectManager()->GetLayersManager()->GetLayers( layers );
	for (int i = 0; i < layers.size(); ++i)
	{
		if(layers[i])
			pEnumVar->AddEnumItem( layers[i]->GetName(), layers[i]->GetName() );
	}
	pEnumVar->Set(val);
	pVarBlock->AddVariable(pEnumVar);
}

void CHyperGraphView::QuickSearchNode( CHyperNode *pNode )
{
	assert(pNode);
	m_pQuickSearchNode = pNode;
	CRect rc = LocalToViewRect( pNode->GetRect() );
	rc.DeflateRect(1,1);
	rc.bottom = rc.top + DEFAULT_EDIT_HEIGHT * m_zoom;

	if (m_quickSearchEdit.m_hWnd)
		m_quickSearchEdit.DestroyWindow();

	m_quickSearchEdit.SetView(this);
	int nEditFlags = WS_CHILD|WS_VISIBLE|ES_AUTOHSCROLL;

	m_quickSearchEdit.Create( nEditFlags,rc,this,IDC_RENAME );
	m_quickSearchEdit.SetWindowText( m_pQuickSearchNode->GetName() );

	HFONT font = ::CreateFont(12 * m_zoom,0,0,0,FW_BOLD,0,0,0,
		DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
		CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY,
		DEFAULT_PITCH, "Ms Shell Dlg 2");

	m_quickSearchEdit.SetFont(CFont::FromHandle(font));
	m_quickSearchEdit.SetCapture();
	m_quickSearchEdit.SetSel(0,-1);
	m_quickSearchEdit.SetFocus();
}

void CHyperGraphView::OnAcceptSearch()
{
	if(m_pQuickSearchNode)
	{
		CPoint point;
		GetCursorPos(&point);
		ScreenToClient(&point);
		CString sName = m_pQuickSearchNode->GetName();

		if(sName.Find("Misc:") >= 0)
			sName.Delete(0,5);

		CQuickSearchNode* pQuickSearchNode = static_cast<CQuickSearchNode*>(m_pQuickSearchNode.get());
		if (pQuickSearchNode && pQuickSearchNode->GetSearchResultCount())
		{
			CreateNode(sName, point);
			m_pGraph->RemoveNode( m_pQuickSearchNode );
			m_pQuickSearchNode = NULL;
		}
	}

	m_quickSearchEdit.DestroyWindow();
	m_SearchResults.clear();
	m_sCurrentSearchSelection = "";

	Invalidate();
}

void CHyperGraphView::OnCancelSearch()
{
	if(m_pQuickSearchNode)
		m_pGraph->RemoveNode( m_pQuickSearchNode );

	m_quickSearchEdit.DestroyWindow();
	m_SearchResults.clear();
	m_sCurrentSearchSelection = "";
	m_pQuickSearchNode = NULL;

	Invalidate();
}

void CHyperGraphView::OnSearchNodes()
{
	if(!m_pGraph || !m_pGraph->GetManager() || !m_pQuickSearchNode)
		return;

	NodeFilter filter(m_componentViewMask);
	std::vector<_smart_ptr<CHyperNode> > prototypes;
	m_pGraph->GetManager()->GetPrototypesEx( prototypes, true, functor_ret(filter, &NodeFilter::Visit) );

	m_SearchResults.clear();
	m_sCurrentSearchSelection = "";
	CString fullname = "";
	CString fullnameLower = "";
	std::vector<CString> searchStrings;
	CString quickSearchtext = "";

	m_quickSearchEdit.GetWindowText(quickSearchtext);
	quickSearchtext.MakeLower();
	
	int pos = 0;
	do
	{
		CString search = quickSearchtext.Tokenize( " ", pos );
		search.Trim();
		searchStrings.push_back(search);		
	}
	while ( pos != -1 );

	for (std::vector<_smart_ptr<CHyperNode> >::iterator iter = prototypes.begin(); iter != prototypes.end(); ++iter)
	{	
		CHyperNode* pPrototypeNode = (*iter).get();

		if(pPrototypeNode && (pPrototypeNode->IsFlowNode() || pPrototypeNode->IsEditorSpecialNode()) && (!pPrototypeNode->IsObsolete() || m_componentViewMask & EFLN_OBSOLETE) )
		{
			fullname = pPrototypeNode->GetClassName();	
			fullnameLower = fullname;
			fullnameLower.MakeLower();

			if(fullnameLower.Find(":") < 0)
			{
				fullnameLower.Insert(0, "misc:");
				fullname.Insert(0, "Misc:");
			}

			bool ok = true;
			for(size_t i=0; i<searchStrings.size(); ++i)
			{
				if (fullnameLower.Find(searchStrings[i]) < 0)
				{
					ok = false;
					break;
				}
			}

			if(ok)
			{
				stl::push_back_unique(m_SearchResults, fullname);
			}
		}		
	}

	CQuickSearchNode* pQuickSearchNode = static_cast<CQuickSearchNode*>(m_pQuickSearchNode.get());

	if(!pQuickSearchNode)
		return;
	
	if(m_SearchResults.size() == 0)
	{
		m_sCurrentSearchSelection = "";
		pQuickSearchNode->SetSearchResultCount(0);
		pQuickSearchNode->SetIndex(0);
		m_pQuickSearchNode->SetName("NO SEARCH RESULTS...");
	}
	else
	{
		pQuickSearchNode->SetSearchResultCount(m_SearchResults.size());
		pQuickSearchNode->SetIndex(1);

		fullname = *(m_SearchResults.begin());
		m_pQuickSearchNode->SetName(fullname);
		m_sCurrentSearchSelection = fullname;
	}
}

void CHyperGraphView::OnSelectNextSearchResult(bool bUp)
{
	if(!m_pQuickSearchNode)
		return;

	CQuickSearchNode* pQuickSearchNode = static_cast<CQuickSearchNode*>(m_pQuickSearchNode.get());

	if(!pQuickSearchNode)
		return;

	if(bUp)
	{
		for (std::vector<CString>::reverse_iterator iter = m_SearchResults.rbegin(); iter != m_SearchResults.rend();++iter)
		{	
			CString name = (*iter);

			if (!strcmp(name, m_sCurrentSearchSelection))
			{
				++iter;
				pQuickSearchNode->SetIndex(pQuickSearchNode->GetIndex()-1);
				if (iter == m_SearchResults.rend())
				{
					iter = m_SearchResults.rbegin();
					pQuickSearchNode->SetIndex(m_SearchResults.size());
				}

				m_pQuickSearchNode->SetName((*iter));
				m_sCurrentSearchSelection = (*iter);
				break;
			}
		}
	}
	else
	{
		for (std::vector<CString>::iterator iter = m_SearchResults.begin(); iter != m_SearchResults.end();++iter)
		{	
			CString name = (*iter);

			if (!strcmp(name, m_sCurrentSearchSelection))
			{
				++iter;
				pQuickSearchNode->SetIndex(pQuickSearchNode->GetIndex()+1);

				if (iter == m_SearchResults.end())
				{
					iter = m_SearchResults.begin();
					pQuickSearchNode->SetIndex(1);
				}

				m_pQuickSearchNode->SetName((*iter));
				m_sCurrentSearchSelection = (*iter);
				break;
			}
		}
	}
}
